mirror of
https://github.com/house-of-abbey/GarminHomeAssistant.git
synced 2025-07-30 00:18:38 +00:00
Improve web: dim disabled, icons, pin
This commit is contained in:
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -2,5 +2,8 @@
|
||||
"cSpell.words": [
|
||||
"usbs",
|
||||
"Venu"
|
||||
]
|
||||
],
|
||||
"files.exclude": {
|
||||
"resources-*": true
|
||||
}
|
||||
}
|
@ -53,7 +53,7 @@
|
||||
Poll delay adds a user configurable delay (in seconds) to each round of
|
||||
status updates of all item in the device's menu that might be amended
|
||||
externally from the watch. A user has requested that it is possible to add
|
||||
this delayfor an "always open" mode of operation, which then drains the
|
||||
this delay for an "always open" mode of operation, which then drains the
|
||||
watch battery from the additional API access activity.
|
||||
-->
|
||||
<property id="poll_delay_combined" type="number">5</property>
|
||||
|
@ -37,7 +37,8 @@
|
||||
margin-left: 0.5em;
|
||||
filter: grayscale() invert();
|
||||
}
|
||||
.template, .info {
|
||||
.template,
|
||||
.info {
|
||||
background-image: url(../resources-icons-48/info_type.svg);
|
||||
background-size: contain;
|
||||
margin-left: 0.5em;
|
||||
@ -63,6 +64,13 @@
|
||||
margin-left: 0.5em;
|
||||
filter: grayscale() invert();
|
||||
}
|
||||
.mdi {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
:root {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
@ -285,6 +293,10 @@
|
||||
background-color: var(--ctp-mocha-overlay1);
|
||||
}
|
||||
</style>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/@mdi/font@7.4.47/css/materialdesignicons.min.css"
|
||||
defer />
|
||||
</head>
|
||||
<body>
|
||||
<div id="settings">
|
||||
@ -441,9 +453,10 @@ http:
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<script src="https://www.unpkg.com/monaco-editor@0.45.0/min/vs/loader.js"></script>
|
||||
<script src="https://www.unpkg.com/monaco-editor@0.52.2/min/vs/loader.js"></script>
|
||||
<script src="https://www.unpkg.com/json-ast-comments@1.1.1/lib/json.js"></script>
|
||||
<script src="https://www.unpkg.com/toastify-js@1.12.0/src/toastify.js"></script>
|
||||
<script src="https://code.iconify.design/1/1.0.6/iconify.min.js"></script>
|
||||
<script type="module" src="./main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
70
web/main.js
70
web/main.js
@ -11,7 +11,7 @@ let api_token = localStorage.getItem('api_token') ?? '';
|
||||
|
||||
/**
|
||||
* Get all entities in HomeAssistant.
|
||||
* @returns {Promise<Record<string, string>>} [id, name]
|
||||
* @returns {Promise<Record<string, { name: string, icon?: string }>>} [id, name]
|
||||
*/
|
||||
async function get_entities() {
|
||||
try {
|
||||
@ -21,7 +21,7 @@ async function get_entities() {
|
||||
Authorization: `Bearer ${api_token}`,
|
||||
},
|
||||
mode: 'cors',
|
||||
body: `{"template":"[{% for entity in states %}[\\"{{ entity.entity_id }}\\",\\"{{ entity.name }}\\"]{% if not loop.last %},{% endif %}{% endfor %}]"}`,
|
||||
body: `{"template":"[{% for entity in states %}[\\"{{ entity.entity_id }}\\",\\"{{ entity.name }}\\",\\"{{ entity.attributes.icon }}\\"]{% if not loop.last %},{% endif %}{% endfor %}]"}`,
|
||||
});
|
||||
if (res.status == 401 || res.status == 403) {
|
||||
document.querySelector('#api_token').classList.add('invalid');
|
||||
@ -29,8 +29,16 @@ async function get_entities() {
|
||||
}
|
||||
document.querySelector('#api_url').classList.remove('invalid');
|
||||
document.querySelector('#api_token').classList.remove('invalid');
|
||||
return Object.fromEntries(await res.json());
|
||||
} catch {
|
||||
const data = {};
|
||||
for (const [id, name, icon] of await res.json()) {
|
||||
data[id] = { name };
|
||||
if (icon !== '') {
|
||||
data[id].icon = icon;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
} catch (e) {
|
||||
console.error('Error fetching entities:', e);
|
||||
document.querySelector('#api_url').classList.add('invalid');
|
||||
return {};
|
||||
}
|
||||
@ -57,7 +65,8 @@ async function get_devices() {
|
||||
document.querySelector('#api_url').classList.remove('invalid');
|
||||
document.querySelector('#api_token').classList.remove('invalid');
|
||||
return Object.fromEntries(await res.json());
|
||||
} catch {
|
||||
} catch (e) {
|
||||
console.error('Error fetching devices:', e);
|
||||
document.querySelector('#api_url').classList.add('invalid');
|
||||
return {};
|
||||
}
|
||||
@ -84,7 +93,8 @@ async function get_areas() {
|
||||
document.querySelector('#api_url').classList.remove('invalid');
|
||||
document.querySelector('#api_token').classList.remove('invalid');
|
||||
return Object.fromEntries(await res.json());
|
||||
} catch {
|
||||
} catch (e) {
|
||||
console.error('Error fetching areas:', e);
|
||||
document.querySelector('#api_url').classList.add('invalid');
|
||||
return {};
|
||||
}
|
||||
@ -119,7 +129,8 @@ async function get_services() {
|
||||
}
|
||||
}
|
||||
return services;
|
||||
} catch {
|
||||
} catch (e) {
|
||||
console.error('Error fetching services:', e);
|
||||
document.querySelector('#api_url').classList.add('invalid');
|
||||
return [];
|
||||
}
|
||||
@ -370,6 +381,9 @@ async function generate_schema(entities, devices, areas, services, schema) {
|
||||
confirm: {
|
||||
$ref: '#/$defs/confirm',
|
||||
},
|
||||
pin: {
|
||||
$ref: '#/$defs/pin',
|
||||
},
|
||||
data: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
@ -724,6 +738,7 @@ require(['vs/editor/editor.main'], async () => {
|
||||
|
||||
var decorations = editor.createDecorationsCollection([]);
|
||||
|
||||
/** @type {monaco.editor.IMarkerData[]} */
|
||||
let markers = [];
|
||||
|
||||
const renderTemplate = editor.addCommand(
|
||||
@ -905,6 +920,7 @@ require(['vs/editor/editor.main'], async () => {
|
||||
const ast = json.parse(model.getValue());
|
||||
const data = JSON.parse(model.getValue());
|
||||
markers = [];
|
||||
/** @type {monaco.editor.IModelDeltaDecoration[]} */
|
||||
const glyphs = [];
|
||||
async function testToggle(range, entity) {
|
||||
const res = await fetch(api_url + '/states/' + entity, {
|
||||
@ -983,8 +999,10 @@ require(['vs/editor/editor.main'], async () => {
|
||||
* @param {import('json-ast-comments').JsonAst |
|
||||
* import('json-ast-comments').JsonProperty} node
|
||||
* @param {string[]} path
|
||||
* @param {import('json-ast-comments').JsonAst |
|
||||
* import('json-ast-comments').JsonProperty | null} parent
|
||||
*/
|
||||
function recurse(node, path) {
|
||||
function recurse(node, path, parent = null) {
|
||||
if (node.type === 'property') {
|
||||
if (node.key[0].value === 'content') {
|
||||
templates.push([
|
||||
@ -1010,11 +1028,39 @@ require(['vs/editor/editor.main'], async () => {
|
||||
}
|
||||
trim++;
|
||||
markers.push({
|
||||
message: entities[node.value[0].value] ?? 'Entity not found',
|
||||
message: entities[node.value[0].value].name ?? 'Entity not found',
|
||||
severity: monaco.MarkerSeverity.Hint,
|
||||
...range,
|
||||
startColumn: trim,
|
||||
});
|
||||
glyphs.push({
|
||||
range,
|
||||
options: {
|
||||
isWholeLine: true,
|
||||
glyphMarginClassName:
|
||||
'mdi ' +
|
||||
entities[node.value[0].value]?.icon?.replace(':', '-'),
|
||||
},
|
||||
});
|
||||
} else if (
|
||||
node.key[0].value === 'enabled' &&
|
||||
node.value[0].type === 'boolean' &&
|
||||
!node.value[0].value
|
||||
) {
|
||||
glyphs.push({
|
||||
range: {
|
||||
startLineNumber: parent.members[0].key[0].range.start.line + 1,
|
||||
startColumn: 0,
|
||||
endLineNumber:
|
||||
parent.members[parent.members.length - 1].value[0].range.end
|
||||
.line + 1,
|
||||
endColumn: 10000,
|
||||
},
|
||||
options: {
|
||||
isWholeLine: true,
|
||||
inlineClassName: 'disabled',
|
||||
},
|
||||
});
|
||||
} else if (node.key[0].value === 'type') {
|
||||
if (node.value[0].value === 'toggle') {
|
||||
toggles.push([
|
||||
@ -1041,15 +1087,15 @@ require(['vs/editor/editor.main'], async () => {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
recurse(node.value[0], [...path, node.key[0].value]);
|
||||
recurse(node.value[0], [...path, node.key[0].value], node);
|
||||
}
|
||||
} else if (node.type === 'array') {
|
||||
for (let i = 0; i < node.members.length; i++) {
|
||||
recurse(node.members[i], [...path, i]);
|
||||
recurse(node.members[i], [...path, i], node);
|
||||
}
|
||||
} else if (node.type === 'object') {
|
||||
for (let member of node.members) {
|
||||
recurse(member, path);
|
||||
recurse(member, path, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,10 +10,11 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/toastify-js": "^1.12.3",
|
||||
"@types/toastify-js": "^1.12.4",
|
||||
"@vscode/webview-ui-toolkit": "1.4.0",
|
||||
"json-ast-comments": "1.1.1",
|
||||
"monaco-editor": "0.45.0",
|
||||
"monaco-editor": "0.52.2",
|
||||
"prettier": "^3.6.2",
|
||||
"serve": "^14.2.1"
|
||||
}
|
||||
}
|
||||
}
|
25
web/pnpm-lock.yaml
generated
25
web/pnpm-lock.yaml
generated
@ -6,8 +6,8 @@ settings:
|
||||
|
||||
devDependencies:
|
||||
'@types/toastify-js':
|
||||
specifier: ^1.12.3
|
||||
version: 1.12.3
|
||||
specifier: ^1.12.4
|
||||
version: 1.12.4
|
||||
'@vscode/webview-ui-toolkit':
|
||||
specifier: 1.4.0
|
||||
version: 1.4.0(react@18.2.0)
|
||||
@ -15,8 +15,11 @@ devDependencies:
|
||||
specifier: 1.1.1
|
||||
version: 1.1.1
|
||||
monaco-editor:
|
||||
specifier: 0.45.0
|
||||
version: 0.45.0
|
||||
specifier: 0.52.2
|
||||
version: 0.52.2
|
||||
prettier:
|
||||
specifier: ^3.6.2
|
||||
version: 3.6.2
|
||||
serve:
|
||||
specifier: ^14.2.1
|
||||
version: 14.2.1
|
||||
@ -52,8 +55,8 @@ packages:
|
||||
exenv-es6: 1.1.1
|
||||
dev: true
|
||||
|
||||
/@types/toastify-js@1.12.3:
|
||||
resolution: {integrity: sha512-9RjLlbAHMSaae/KZNHGv19VG4gcLIm3YjvacCXBtfMfYn26h76YP5oxXI8k26q4iKXCB9LNfv18lsoS0JnFPTg==}
|
||||
/@types/toastify-js@1.12.4:
|
||||
resolution: {integrity: sha512-zfZHU4tKffPCnZRe7pjv/eFKzTVHozKewFCKaCjZ4gFinKgJRz/t0bkZiMCXJxPhv/ZoeDGNOeRD09R0kQZ/nw==}
|
||||
dev: true
|
||||
|
||||
/@vscode/webview-ui-toolkit@1.4.0(react@18.2.0):
|
||||
@ -415,8 +418,8 @@ packages:
|
||||
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
||||
dev: true
|
||||
|
||||
/monaco-editor@0.45.0:
|
||||
resolution: {integrity: sha512-mjv1G1ZzfEE3k9HZN0dQ2olMdwIfaeAAjFiwNprLfYNRSz7ctv9XuCT7gPtBGrMUeV1/iZzYKj17Khu1hxoHOA==}
|
||||
/monaco-editor@0.52.2:
|
||||
resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==}
|
||||
dev: true
|
||||
|
||||
/ms@2.0.0:
|
||||
@ -460,6 +463,12 @@ packages:
|
||||
resolution: {integrity: sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==}
|
||||
dev: true
|
||||
|
||||
/prettier@3.6.2:
|
||||
resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/punycode@1.4.1:
|
||||
resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
|
||||
dev: true
|
||||
|
Reference in New Issue
Block a user