iobroker.lovelace 4.1.4 → 4.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -44
- package/io-package.json +27 -27
- package/lib/converters/genericConverter.js +1 -1
- package/lib/converters/light.js +117 -54
- package/lib/server.js +126 -127
- package/main.js +20 -19
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -11,16 +11,16 @@
|
|
|
11
11
|
|
|
12
12
|
## lovelace adapter for ioBroker
|
|
13
13
|
|
|
14
|
-
With this adapter you can build visualization for ioBroker with Home Assistant Lovelace UI.
|
|
14
|
+
With this adapter, you can build visualization for ioBroker with Home Assistant Lovelace UI.
|
|
15
15
|
|
|
16
16
|
[Deutsche Dokumentation](docs/de/README.md)
|
|
17
17
|
|
|
18
18
|
## Instance objects
|
|
19
|
-
In the folder instances there are some objects that can be used to control the UI. For every browser a new subfolder will
|
|
20
|
-
be created with a random ID. This ID is stored in the client browser's web storage. If you delete the web storage a new
|
|
19
|
+
In the folder instances, there are some objects that can be used to control the UI. For every browser, a new subfolder will
|
|
20
|
+
be created with a random ID. This ID is stored in the client browser's web storage. If you delete the web storage, a new
|
|
21
21
|
instance will be created. If you use Fully Kiosk Browser make sure the function `Delete webstorage on reload` is **disabled**.
|
|
22
22
|
|
|
23
|
-
This functionality uses browser_mod, which is installed and updated by the adapter. Do not add your own version of browser_mod as custom card.
|
|
23
|
+
This functionality uses browser_mod, which is installed and updated by the adapter. Do not add your own version of browser_mod as a custom card.
|
|
24
24
|
|
|
25
25
|
## Configuration
|
|
26
26
|
There are two ways how the entities could be configured:
|
|
@@ -35,7 +35,7 @@ In auto mode the similar process will be applied like for `google home` or `mate
|
|
|
35
35
|
You can define friendly names and this will be used in entities.
|
|
36
36
|
|
|
37
37
|
### Manual
|
|
38
|
-
The objects can be defined manually in object tree like sql or histroy. The type of entity must be provided and optionally the name of object.
|
|
38
|
+
The objects can be defined manually in an object tree like sql or histroy. The type of entity must be provided and optionally the name of object.
|
|
39
39
|
With this method only simple entities, like input_number, input_text or input_boolean could be created. It may not have more than one state or attribute.
|
|
40
40
|
|
|
41
41
|
## Panels
|
|
@@ -96,8 +96,8 @@ common: {
|
|
|
96
96
|
```
|
|
97
97
|
|
|
98
98
|
### Select input
|
|
99
|
-
This can be done manually if input_select entity type in custom dialog is selected.
|
|
100
|
-
The list of options to select from should be
|
|
99
|
+
This can be done manually if `input_select` entity type in custom dialog is selected.
|
|
100
|
+
The list of options to select from should be provided in a standard `common.states` object:
|
|
101
101
|
|
|
102
102
|
```
|
|
103
103
|
"common": {
|
|
@@ -115,10 +115,10 @@ The list of options to select from should be provide in standard commom.states o
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
```
|
|
118
|
-
in other words
|
|
118
|
+
in other words, there should also be select input in IoB.
|
|
119
119
|
|
|
120
120
|
### Timer
|
|
121
|
-
Timer could be simulated by following script:
|
|
121
|
+
Timer could be simulated by the following script:
|
|
122
122
|
|
|
123
123
|
```
|
|
124
124
|
createState(
|
|
@@ -202,11 +202,11 @@ createState(
|
|
|
202
202
|
```
|
|
203
203
|
|
|
204
204
|
### Weather
|
|
205
|
-
Tested with yr and daswetter
|
|
206
|
-
- daswetter.0.NextDays.Location_1
|
|
207
|
-
- yr.0.forecast
|
|
205
|
+
Tested with `yr` and `daswetter`. One or more of the following objects must have `Function=Weather` and `Room=Any` set to be available in configuration:
|
|
206
|
+
- `daswetter.0.NextDays.Location_1`
|
|
207
|
+
- `yr.0.forecast`
|
|
208
208
|
|
|
209
|
-
Tested with AccuWeather driver v1.1.0 https://github.com/iobroker-community-adapters/ioBroker.accuweather.
|
|
209
|
+
Tested with `AccuWeather` driver v1.1.0 https://github.com/iobroker-community-adapters/ioBroker.accuweather.
|
|
210
210
|
Custom Lovelace card created in support of accuweather forecast - https://github.com/algar42/IoB.lovelace.accuweather-card
|
|
211
211
|
|
|
212
212
|
### Shopping list
|
|
@@ -235,7 +235,7 @@ createState('location', '39.5681295;2.6432632', false, {
|
|
|
235
235
|
});
|
|
236
236
|
```
|
|
237
237
|
|
|
238
|
-
or
|
|
238
|
+
or these two objects:
|
|
239
239
|
|
|
240
240
|
```
|
|
241
241
|
createState('location.longitude', 2.6432632, false, {
|
|
@@ -257,7 +257,7 @@ createState('location.latitude', 39.5681295, false, {
|
|
|
257
257
|
```
|
|
258
258
|
|
|
259
259
|
### Picture entity
|
|
260
|
-
You can use static picture for it or use any state that delivers URL as state.
|
|
260
|
+
You can use static picture for it or use any state that delivers URL as a state.
|
|
261
261
|
E.g.:
|
|
262
262
|
|
|
263
263
|
```
|
|
@@ -275,34 +275,34 @@ E.g.:
|
|
|
275
275
|
}
|
|
276
276
|
```
|
|
277
277
|
|
|
278
|
-
or just set
|
|
278
|
+
or just manually set the entity type to `camera` and write URL into it.
|
|
279
279
|
|
|
280
280
|
### Markdown
|
|
281
281
|
You can use bindings in markdown like in [iobroker.vis](https://github.com/ioBroker/ioBroker.vis#bindings-of-objects).
|
|
282
282
|
|
|
283
|
-
E.g
|
|
283
|
+
E.g., Text `Admin adapter is {a:system.adapter.admin.0.alive;a === true || a === 'true' ? ' ' : 'not '} *alive*.` will produce text `Admin adapter is alive` in a markdown panel.
|
|
284
284
|
|
|
285
285
|
## Custom cards
|
|
286
286
|
### Upload of custom cards
|
|
287
|
-
To upload the custom card write following:
|
|
287
|
+
To upload the custom card, write the following:
|
|
288
288
|
|
|
289
289
|
```iobroker file write PATH_TO_FILE\bignumber-card.js /lovelace.0/cards/```
|
|
290
290
|
|
|
291
291
|
After restart of lovelace adapter it will include all files from the `cards` directory automatically.
|
|
292
292
|
|
|
293
|
-
|
|
294
|
-
- bignumber-card
|
|
295
|
-
- simple-thermostat
|
|
296
|
-
- thermostat
|
|
293
|
+
The following custom cards could be tested successfully:
|
|
294
|
+
- `bignumber-card`: https://github.com/custom-cards/bignumber-card/blob/master/bignumber-card.js
|
|
295
|
+
- `simple-thermostat`: https://github.com/nervetattoo/simple-thermostat/releases (take the latest release)
|
|
296
|
+
- `thermostat`: https://github.com/ciotlosm/custom-lovelace/tree/master/thermostat-card (both files .js and .lib.js are required)
|
|
297
297
|
|
|
298
298
|
I found this link https://github.com/jimz011/homeassistant as an interesting resource for custom cards.
|
|
299
299
|
|
|
300
|
-
Often the custom cards are stored on
|
|
301
|
-
You should check the `Releases` menu on
|
|
300
|
+
Often the custom cards are stored on GitHub as sources and must be compiled before use.
|
|
301
|
+
You should check the `Releases` menu on GitHub and try to find compiled files there.
|
|
302
302
|
Like this one: [https://github.com/kalkih/mini-graph-card/releases](https://github.com/kalkih/mini-graph-card/releases) (Look for the file `mini-graph-card-bundle.js`)
|
|
303
303
|
|
|
304
304
|
## Own images
|
|
305
|
-
The custom images (e.g
|
|
305
|
+
The custom images (e.g., for a background) could be loaded via the same configuration dialog like the custom cards. And use it like this:
|
|
306
306
|
|
|
307
307
|
`background: center / cover no-repeat url("/cards/background.jpg") fixed`
|
|
308
308
|
|
|
@@ -310,10 +310,10 @@ or
|
|
|
310
310
|
|
|
311
311
|
`background: center / cover no-repeat url("/local/custom_ui/background.jpg") fixed`
|
|
312
312
|
|
|
313
|
-
in lovelace configuration file. Read more about background in lovelace [here](https://www.home-assistant.io/lovelace/views/#background).
|
|
313
|
+
in lovelace configuration file. Read more about the background in lovelace [here](https://www.home-assistant.io/lovelace/views/#background).
|
|
314
314
|
|
|
315
315
|
## Themes
|
|
316
|
-
The themes can be defined in configuration dialog of ioBroker.
|
|
316
|
+
The themes can be defined in the configuration dialog of ioBroker.
|
|
317
317
|
Paste something like:
|
|
318
318
|
```
|
|
319
319
|
midnight:
|
|
@@ -386,7 +386,7 @@ midnight:
|
|
|
386
386
|
taken from [here](https://community.home-assistant.io/t/midnight-theme/28598/2).
|
|
387
387
|
|
|
388
388
|
## Icons
|
|
389
|
-
Use icons in form `mdi:NAME`, like
|
|
389
|
+
Use icons in form `mdi:NAME`, like `mdi:play-network`. Names can be taken from here: https://materialdesignicons.com/
|
|
390
390
|
|
|
391
391
|
## Notifications
|
|
392
392
|
You can add notifications via `sendTo` functionality or by writing the state into `lovelace.X.notifications.add`:
|
|
@@ -419,14 +419,14 @@ on({id: 'lovelace.0.conversation', ack: false, change: 'any'}, obj => {
|
|
|
419
419
|
|
|
420
420
|
## Trouble Shooting
|
|
421
421
|
If you messed up the YAML Code and see a blank page but still have the top menu, you can enable edit mode (if not already enabled) from the menu and then open the menu again to access the "RAW Yaml Editor" in which you see the complete YAML code and can clean it up.
|
|
422
|
-
If that does not help, you can open the object lovelace.*.configuration in raw-editor in ioBroker and have a look there.
|
|
422
|
+
If that does not help, you can open the object `lovelace.*.configuration` in raw-editor in ioBroker and have a look there.
|
|
423
423
|
You can also restore that object from a backup. It contains the complete configuration of your visualization.
|
|
424
424
|
|
|
425
425
|
## Original sources for lovelace
|
|
426
426
|
Used sources are here https://github.com/GermanBluefox/home-assistant-polymer .
|
|
427
427
|
|
|
428
428
|
## Todo
|
|
429
|
-
Security must be taken from current user and not from default_user
|
|
429
|
+
Security must be taken from the current user and not from default_user
|
|
430
430
|
|
|
431
431
|
## Development
|
|
432
432
|
### Version
|
|
@@ -434,7 +434,7 @@ Used version of home-assistant-frontend@20231208.2
|
|
|
434
434
|
Version of Browser Mod: 2.3.0
|
|
435
435
|
|
|
436
436
|
### How to build the new Lovelace version
|
|
437
|
-
First of all the actual https://github.com/home-assistant/frontend (dev branch) must be **manually** merged into https://github.com/GermanBluefox/home-assistant-polymer.git (***iob*** branch!).
|
|
437
|
+
First of all, the actual https://github.com/home-assistant/frontend (dev branch) must be **manually** merged into https://github.com/GermanBluefox/home-assistant-polymer.git (***iob*** branch!).
|
|
438
438
|
|
|
439
439
|
All changes for ioBroker are marked with comment `// IoB`.
|
|
440
440
|
For now (20231208.2) following files were modified:
|
|
@@ -443,14 +443,14 @@ For now (20231208.2) following files were modified:
|
|
|
443
443
|
- `src/data/weather.ts` - add support to display weather icon from url.
|
|
444
444
|
- `src/dialogs/more-info/const.ts` - remove weather state & history
|
|
445
445
|
- `src/dialogs/more-info/ha-more-info-dialog.ts` - remove entity settings button and tab
|
|
446
|
-
- `src/dialogs/more-info/ha-more-info-history.ts` - remove
|
|
446
|
+
- `src/dialogs/more-info/ha-more-info-history.ts` - remove `show more` link in history
|
|
447
447
|
- `src/dialogs/more-info/controls/more-info-weather.ts` - add support to display weather icon from url.
|
|
448
448
|
- `src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts` - disable configuration of voice assistants
|
|
449
449
|
- `src/entrypoints/core.ts` - modified authentication process
|
|
450
450
|
- `src/panels/lovelace/cards/hui-weather-forecast-card.ts` - add support to display weather icon from url.
|
|
451
451
|
- `src/panels/lovelace/entity-rows/hui-weather-entity-row.ts` - add support to display weather icon from url with auth.
|
|
452
452
|
- `src/panels/lovelace/hui-root.ts` - added notifications and voice control
|
|
453
|
-
- `src/util/documentation-url.ts` - for link to iobroker help instead of
|
|
453
|
+
- `src/util/documentation-url.ts` - for link to iobroker help instead of home assistant.
|
|
454
454
|
- `.gitignore` - add `.idea` ignore
|
|
455
455
|
- `.husky/pre-commit` - remove git commit hooks.
|
|
456
456
|
- `package.json` - remove husky commit hook
|
|
@@ -465,14 +465,21 @@ After that checkout modified version in `./build` folder. Then.
|
|
|
465
465
|
6. `gulp build-app` for release or `gulp develop-iob` for the debugging version. To build web after changes you can call `webpack-dev-app` for faster build, but you need to call `build-app` anyway after the version is ready for use.
|
|
466
466
|
7. copy all files from `./build/home-assistant-polymer/hass_frontend` into `./hass_frontend` in this repo
|
|
467
467
|
8. Run `gulp rename` task multiple times (until no changes happen).
|
|
468
|
-
9. Update
|
|
468
|
+
9. Update version in `README.md` and also in `server.js` the `VERSION` constant.
|
|
469
469
|
|
|
470
470
|
## Changelog
|
|
471
471
|
|
|
472
472
|
<!--
|
|
473
|
-
PLACEHOLDER for next version:
|
|
473
|
+
PLACEHOLDER for the next version:
|
|
474
474
|
### **WORK IN PROGRESS**
|
|
475
475
|
-->
|
|
476
|
+
### 4.1.6 (2024-03-07)
|
|
477
|
+
* (Garfonso) remove exessive logging
|
|
478
|
+
* (Garfonso) improve fix for crash again.
|
|
479
|
+
|
|
480
|
+
### 4.1.5 (2024-03-05)
|
|
481
|
+
* (Garfonso) fixed: possible crashes during startup
|
|
482
|
+
|
|
476
483
|
### 4.1.4 (2024-02-10)
|
|
477
484
|
* (Garfonso) improved fix: lamp icons now turn gray on switch off.
|
|
478
485
|
|
|
@@ -484,15 +491,6 @@ After that checkout modified version in `./build` folder. Then.
|
|
|
484
491
|
### 4.1.2 (2024-01-09)
|
|
485
492
|
* (Garfonso) fix: time in timestamp display
|
|
486
493
|
|
|
487
|
-
### 4.1.1 (2024-01-02)
|
|
488
|
-
* (Garfonso) changed: determining user id
|
|
489
|
-
* (Garfosno) changed: history attributes handling
|
|
490
|
-
* (Garfonso) added: handle browser_mod/recall_id service call.
|
|
491
|
-
* (Garfonso) changed: all states are strings (fixes #483)
|
|
492
|
-
|
|
493
|
-
### 4.1.0 (2023-12-18)
|
|
494
|
-
* (Garfons) add an option to show users on login screen (off by default)
|
|
495
|
-
|
|
496
494
|
## License
|
|
497
495
|
|
|
498
496
|
Copyright 2019-2024, bluefox <dogafox@gmail.com>
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "lovelace",
|
|
4
|
-
"version": "4.1.
|
|
4
|
+
"version": "4.1.6",
|
|
5
5
|
"news": {
|
|
6
|
+
"4.1.6": {
|
|
7
|
+
"en": "remove exessive logging\nimprove fix for crash again.",
|
|
8
|
+
"de": "exessive log ausgabe entfernen\nverbesserung der reparatur für crash.",
|
|
9
|
+
"ru": "удалить exessive logging\nулучшить исправление для аварии снова.",
|
|
10
|
+
"pt": "remover registro exessivo\nmelhorar a correção para acidente novamente.",
|
|
11
|
+
"nl": "exessieve logging verwijderen\nverbeteren fix voor crash opnieuw.",
|
|
12
|
+
"fr": "supprimer l'exploitation exessive\naméliorer la correction pour le crash à nouveau.",
|
|
13
|
+
"it": "rimuovere logging exessive\nmigliorare la correzione per crash di nuovo.",
|
|
14
|
+
"es": "remove exessive logging\nmejorar la solución para el accidente de nuevo.",
|
|
15
|
+
"pl": "usunięcie nadmiernego logowania\npoprawić naprawić ponownie awarii.",
|
|
16
|
+
"uk": "видалити есенціальний журнал\nзнову поліпшуйте фіксацію.",
|
|
17
|
+
"zh-cn": "删除 exessive 日志\n改善再次崩溃的修复."
|
|
18
|
+
},
|
|
19
|
+
"4.1.5": {
|
|
20
|
+
"en": "fixed: possible crashes during startup",
|
|
21
|
+
"de": "behoben: mögliche abstürzen beim start",
|
|
22
|
+
"ru": "фиксированный: возможные аварии во время запуска",
|
|
23
|
+
"pt": "corrigido: possíveis falhas durante a inicialização",
|
|
24
|
+
"nl": "vast: mogelijke crashes tijdens opstarten",
|
|
25
|
+
"fr": "corrigé: crashs possibles pendant le démarrage",
|
|
26
|
+
"it": "fisso: possibili crash durante l'avvio",
|
|
27
|
+
"es": "fijo: posibles fallos durante la puesta en marcha",
|
|
28
|
+
"pl": "stałe: możliwe awarie podczas uruchamiania",
|
|
29
|
+
"uk": "виправлено: можливі аварії під час запуску",
|
|
30
|
+
"zh-cn": "固定: 启动时可能发生的崩溃"
|
|
31
|
+
},
|
|
6
32
|
"4.1.4": {
|
|
7
33
|
"en": "improved fix: lamp icons now turn gray on switch off.",
|
|
8
34
|
"de": "verbesserter fix: lampensymbole werden nun grau beim ausschalten."
|
|
@@ -31,32 +57,6 @@
|
|
|
31
57
|
"4.1.0": {
|
|
32
58
|
"en": "add an option to show users on login screen (off by default)",
|
|
33
59
|
"de": "Neu Option um Benutzer auf dem Login-Bildschirm anzuzeigen"
|
|
34
|
-
},
|
|
35
|
-
"4.0.12": {
|
|
36
|
-
"en": "fix lint errors",
|
|
37
|
-
"de": "lint fehler beheben",
|
|
38
|
-
"ru": "исправить ошибки lint",
|
|
39
|
-
"pt": "corrigir erros de lint",
|
|
40
|
-
"nl": "lint fouten",
|
|
41
|
-
"fr": "corriger les erreurs lint",
|
|
42
|
-
"it": "correggere gli errori di lintuizione",
|
|
43
|
-
"es": "corregir errores lint",
|
|
44
|
-
"pl": "błędy lintowe",
|
|
45
|
-
"uk": "виправлено помилки в розмітці",
|
|
46
|
-
"zh-cn": "f 固定错误"
|
|
47
|
-
},
|
|
48
|
-
"4.0.11": {
|
|
49
|
-
"en": "updated dependencies",
|
|
50
|
-
"de": "aktualisierte abhängigkeiten",
|
|
51
|
-
"ru": "обновленные зависимости",
|
|
52
|
-
"pt": "dependências atualizadas",
|
|
53
|
-
"nl": "vertaling:",
|
|
54
|
-
"fr": "dépendances actualisées",
|
|
55
|
-
"it": "dipendenze aggiornate",
|
|
56
|
-
"es": "dependencias actualizadas",
|
|
57
|
-
"pl": "zależności aktualizacji",
|
|
58
|
-
"uk": "оновлені залежності",
|
|
59
|
-
"zh-cn": "二. 最新依赖"
|
|
60
60
|
}
|
|
61
61
|
},
|
|
62
62
|
"title": "Visualization with Lovelace-UI",
|
|
@@ -59,7 +59,7 @@ exports.iobState2EntityState = function (entity, val, attribute) {
|
|
|
59
59
|
return val ? 'on' : 'off';
|
|
60
60
|
} else if (type === 'binary_sensor') {
|
|
61
61
|
return val ? 'on' : 'off';
|
|
62
|
-
}else if (typeof val === 'number' && entity.attributes && ['date', 'timestamp'].includes(entity.attributes.device_class)) {
|
|
62
|
+
} else if (typeof val === 'number' && entity.attributes && ['date', 'timestamp'].includes(entity.attributes.device_class)) {
|
|
63
63
|
//convert to date string
|
|
64
64
|
const date = new Date(val);
|
|
65
65
|
let dateStr = date.toDateString();
|
package/lib/converters/light.js
CHANGED
|
@@ -42,7 +42,26 @@ const SUPPORT_EFFECT = 4;
|
|
|
42
42
|
//const SUPPORT_FLASH = 8;
|
|
43
43
|
//const SUPPORT_TRANSITION = 32;
|
|
44
44
|
|
|
45
|
-
const attributesToNullOnOff = ['color_mode', 'brightness', 'color_temp', 'hs_color', 'rgb_color', 'rgbw_color', 'xy_color', 'effect'];
|
|
45
|
+
const attributesToNullOnOff = ['color_mode', 'brightness', 'color_temp', 'color_temp_kelvin', 'hs_color', 'rgb_color', 'rgbw_color', 'xy_color', 'effect'];
|
|
46
|
+
|
|
47
|
+
function clearOrRestoreAttributes(entity) {
|
|
48
|
+
if (entity.state === 'on') {
|
|
49
|
+
for (const attr of attributesToNullOnOff) {
|
|
50
|
+
if (entity.attributes[attr] === null || entity.attributes[attr] === undefined) { //make sure the attribute was not yet read from somewhere else
|
|
51
|
+
entity.attributes[attr] = entity.context.STATE.storedValues[attr];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//adapterData.log.debug(`Stored old values for next switch on: ${JSON.stringify(entity.context.STATE.storedValues)}`);
|
|
55
|
+
} else {
|
|
56
|
+
for (const attr of attributesToNullOnOff) {
|
|
57
|
+
if (entity.attributes[attr] !== undefined) {
|
|
58
|
+
entity.context.STATE.storedValues[attr] = entity.attributes[attr];
|
|
59
|
+
entity.attributes[attr] = null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//adapterData.log.debug(`Restored old values from previous switch off: ${JSON.stringify(entity.attributes)}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
46
65
|
|
|
47
66
|
function _lightAdvancedAddState(states, objects, entity) {
|
|
48
67
|
const getState = states.stateRead;
|
|
@@ -53,31 +72,18 @@ function _lightAdvancedAddState(states, objects, entity) {
|
|
|
53
72
|
if (!state) {
|
|
54
73
|
state = {val: null};
|
|
55
74
|
}
|
|
56
|
-
const oldState = entity.state;
|
|
57
75
|
entity.state = state.val ? 'on' : 'off';
|
|
58
|
-
|
|
59
|
-
for (const attr of attributesToNullOnOff) {
|
|
60
|
-
if (entity.attributes[attr] !== undefined) {
|
|
61
|
-
entity.context.STATE.storedValues[attr] = entity.attributes[attr];
|
|
62
|
-
entity.attributes[attr] = null;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
adapterData.log.debug(`Stored old values for next switch on: ${JSON.stringify(entity.context.STATE.storedValues)}`);
|
|
66
|
-
}
|
|
67
|
-
if (state.val && oldState !== 'on') {
|
|
68
|
-
for (const attr of attributesToNullOnOff) {
|
|
69
|
-
if (entity.attributes[attr] === null) { //make sure the attribute was not yet read from somewhere else
|
|
70
|
-
entity.attributes[attr] = entity.context.STATE.storedValues[attr];
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
adapterData.log.debug(`Restored old values from previous switch off: ${JSON.stringify(entity.context.STATE.storedValues)}`);
|
|
74
|
-
}
|
|
76
|
+
clearOrRestoreAttributes(entity);
|
|
75
77
|
};
|
|
76
78
|
|
|
77
79
|
//prevent zigbee 'available' to become getId:
|
|
78
80
|
if (getState && getState.startsWith('zigbee.') && (getState.endsWith('.available') || getState.endsWith('.device_query'))) {
|
|
79
81
|
entity.context.STATE.getId = states.state;
|
|
80
82
|
}
|
|
83
|
+
|
|
84
|
+
//clear initial attributes
|
|
85
|
+
clearOrRestoreAttributes(entity);
|
|
86
|
+
|
|
81
87
|
if (states.state) {
|
|
82
88
|
entity.context.STATE.isBoolean = objects[states.state] && objects[states.state].common && objects[states.state].common.type === 'boolean';
|
|
83
89
|
entity.attributes.supported_color_modes.push(ONOFF);
|
|
@@ -96,8 +102,13 @@ function _lightAdvancedAddColorTemperature(states, objects, entity) {
|
|
|
96
102
|
const tempObj = objects[states.color_temp];
|
|
97
103
|
attribute.convert_to_mired = tempObj && tempObj.common ? (tempObj.common.unit === 'mired') : false;
|
|
98
104
|
attribute.getParser = (entity, attr, state) => {
|
|
105
|
+
let targetAttributes = entity.attributes;
|
|
106
|
+
if (entity.state !== 'on') {
|
|
107
|
+
targetAttributes = entity.context.STATE.storedValues;
|
|
108
|
+
}
|
|
109
|
+
|
|
99
110
|
if (!state || !state.val) {
|
|
100
|
-
|
|
111
|
+
targetAttributes.color_temp = iobMinValueKelvin;
|
|
101
112
|
return;
|
|
102
113
|
}
|
|
103
114
|
let targetCt = state.val;
|
|
@@ -111,9 +122,9 @@ function _lightAdvancedAddColorTemperature(states, objects, entity) {
|
|
|
111
122
|
if (attr.convert_to_mired) {
|
|
112
123
|
targetCt = 1e6 / targetCt;
|
|
113
124
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
125
|
+
targetAttributes.color_temp_kelvin = targetCt;
|
|
126
|
+
targetAttributes.color_temp = 1e6 / targetCt;
|
|
127
|
+
targetAttributes.color_mode = COLOR_TEMP;
|
|
117
128
|
};
|
|
118
129
|
|
|
119
130
|
entity.attributes.max_color_temp_kelvin = tempObj && tempObj.common && tempObj.common.max || iobMaxValueKelvin;
|
|
@@ -132,7 +143,7 @@ function _lightAdvancedAddColorTemperature(states, objects, entity) {
|
|
|
132
143
|
adapterData.log.debug(entity.entity_id + ' ct needs mired conversion: ' + attribute.convert_to_mired);
|
|
133
144
|
|
|
134
145
|
if (entity.attributes.min_color_temp_kelvin > entity.attributes.max_color_temp_kelvin) {
|
|
135
|
-
//for kelvin conversion min and max need to be swapped.
|
|
146
|
+
//for kelvin conversion, min and max need to be swapped.
|
|
136
147
|
const max = entity.attributes.min_color_temp_kelvin;
|
|
137
148
|
entity.attributes.min_color_temp_kelvin = entity.attributes.max_color_temp_kelvin;
|
|
138
149
|
entity.attributes.max_color_temp_kelvin = max;
|
|
@@ -153,13 +164,24 @@ function _lightAdvancedAddBrightness(states, objects, entity) {
|
|
|
153
164
|
if (states.brightness) {
|
|
154
165
|
const attribute = entity.context.ATTRIBUTES.find(a => a.attribute === 'brightness');
|
|
155
166
|
attribute.getParser = (entity, attr, state) => {
|
|
167
|
+
let targetAttributes = entity.attributes;
|
|
168
|
+
if (entity.state !== 'on') {
|
|
169
|
+
targetAttributes = entity.context.STATE.storedValues;
|
|
170
|
+
}
|
|
171
|
+
|
|
156
172
|
state = state || {val: 0};
|
|
157
|
-
|
|
158
|
-
if (
|
|
159
|
-
|
|
173
|
+
targetAttributes.brightness = ((state.val - attr.min) / (attr.max - attr.min)) * 255;
|
|
174
|
+
if (!targetAttributes.color_mode || targetAttributes.color_mode === ONOFF) {
|
|
175
|
+
targetAttributes.color_mode = BRIGHTNESS;
|
|
160
176
|
}
|
|
161
|
-
|
|
162
|
-
|
|
177
|
+
|
|
178
|
+
if (states.state === states.brightness) {
|
|
179
|
+
entity.state = targetAttributes.brightness > 0 ? 'on' : 'off';
|
|
180
|
+
clearOrRestoreAttributes(entity);
|
|
181
|
+
|
|
182
|
+
if (entity.attributes.color_mode === ONOFF) {
|
|
183
|
+
entity.attributes.color_mode = BRIGHTNESS;
|
|
184
|
+
}
|
|
163
185
|
}
|
|
164
186
|
};
|
|
165
187
|
attribute.setId = states.brightness;
|
|
@@ -167,6 +189,11 @@ function _lightAdvancedAddBrightness(states, objects, entity) {
|
|
|
167
189
|
attribute.min = (objects[attribute.getId] && objects[attribute.getId].common && objects[attribute.getId].common.min) || 0;
|
|
168
190
|
attribute.max = (objects[attribute.getId] && objects[attribute.getId].common && objects[attribute.getId].common.max) || 100;
|
|
169
191
|
entity.attributes.supported_color_modes.push(BRIGHTNESS);
|
|
192
|
+
|
|
193
|
+
if (states.state === states.brightness) {
|
|
194
|
+
//clear initial attributes
|
|
195
|
+
clearOrRestoreAttributes(entity);
|
|
196
|
+
}
|
|
170
197
|
}
|
|
171
198
|
}
|
|
172
199
|
|
|
@@ -175,12 +202,21 @@ function _lightAdvancedAddHueAndSat(states, objects, entity) {
|
|
|
175
202
|
const hue_attr = entity.context.ATTRIBUTES.find(a => a.attribute === 'hue');
|
|
176
203
|
hue_attr.max = objects[states.hue] && objects[states.hue].common && objects[states.hue].common.max || 360;
|
|
177
204
|
hue_attr.getParser = (entity, attr, state) => {
|
|
205
|
+
let targetAttributes = entity.attributes;
|
|
206
|
+
if (entity.state !== 'on') {
|
|
207
|
+
targetAttributes = entity.context.STATE.storedValues;
|
|
208
|
+
}
|
|
209
|
+
|
|
178
210
|
state = state || {val: 0};
|
|
179
|
-
|
|
180
|
-
|
|
211
|
+
if (!targetAttributes.hs_color) {
|
|
212
|
+
targetAttributes.hs_color = [0, 100];
|
|
213
|
+
}
|
|
214
|
+
targetAttributes.hs_color[0] = state.val / attr.max * 360;
|
|
215
|
+
targetAttributes.color_mode = HS;
|
|
181
216
|
};
|
|
182
217
|
entity.attributes.supported_color_modes.push(HS);
|
|
183
|
-
entity.attributes.hs_color = [
|
|
218
|
+
entity.attributes.hs_color = [null,null];
|
|
219
|
+
entity.context.STATE.storedValues.hs_color = [0,100];
|
|
184
220
|
}
|
|
185
221
|
|
|
186
222
|
//add saturation as own attribute. Will update saturation values from ioBroker correctly.
|
|
@@ -193,9 +229,13 @@ function _lightAdvancedAddHueAndSat(states, objects, entity) {
|
|
|
193
229
|
return;
|
|
194
230
|
}
|
|
195
231
|
sat_attr.getParser = (entity, attr, state) => {
|
|
232
|
+
let targetAttributes = entity.attributes;
|
|
233
|
+
if (entity.state !== 'on') {
|
|
234
|
+
targetAttributes = entity.context.STATE.storedValues;
|
|
235
|
+
}
|
|
196
236
|
state = state || {val: 0};
|
|
197
|
-
|
|
198
|
-
|
|
237
|
+
targetAttributes.hs_color[1] = state.val / attr.max * 100;
|
|
238
|
+
targetAttributes.color_mode = HS;
|
|
199
239
|
};
|
|
200
240
|
} else {
|
|
201
241
|
sat_attr.getParser = () => {}; //ignore saturation updates.
|
|
@@ -211,6 +251,10 @@ async function _lightAdvancedAddRGBSingle(states, objects, entity) {
|
|
|
211
251
|
attribute.is_rgb_array = false;
|
|
212
252
|
attribute.is_rgb_string = true;
|
|
213
253
|
attribute.getParser = (entity, attr, state) => {
|
|
254
|
+
let targetAttributes = entity.attributes;
|
|
255
|
+
if (entity.state !== 'on') {
|
|
256
|
+
targetAttributes = entity.context.STATE.storedValues;
|
|
257
|
+
}
|
|
214
258
|
let str = state ? (state.val || '#000000').toString() : '#000000';
|
|
215
259
|
if (str[0] === '#') {
|
|
216
260
|
str = str.substring(1);
|
|
@@ -229,23 +273,24 @@ async function _lightAdvancedAddRGBSingle(states, objects, entity) {
|
|
|
229
273
|
b = parseInt(str.substring(4, 6), 16);
|
|
230
274
|
}
|
|
231
275
|
|
|
232
|
-
|
|
233
|
-
|
|
276
|
+
targetAttributes.rgb_color = [r, g, b];
|
|
277
|
+
targetAttributes.color_mode = RGB;
|
|
234
278
|
if (states.white) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
279
|
+
targetAttributes.color_mode = RGBW;
|
|
280
|
+
targetAttributes.rgbw_color[0] = r;
|
|
281
|
+
targetAttributes.rgbw_color[1] = g;
|
|
282
|
+
targetAttributes.rgbw_color[2] = b;
|
|
239
283
|
}
|
|
240
284
|
};
|
|
241
285
|
|
|
242
|
-
//check if current state is rgb array.
|
|
286
|
+
//check if the current state is rgb array.
|
|
243
287
|
const rgbState = await adapterData.adapter.getForeignStateAsync(states.rgb_color);
|
|
244
288
|
if (rgbState && rgbState.val) {
|
|
245
289
|
attribute.is_rgb_array = /([0-9]){1,3},([0-9]){1,3},([0-9]){1,3}/.test(rgbState.val.toString());
|
|
246
290
|
}
|
|
247
291
|
|
|
248
|
-
entity.attributes.rgb_color = [
|
|
292
|
+
entity.attributes.rgb_color = [null,null,null];
|
|
293
|
+
entity.context.STATE.storedValues.rgb_color = [0,0,0];
|
|
249
294
|
entity.attributes.supported_color_modes.push(RGB);
|
|
250
295
|
}
|
|
251
296
|
}
|
|
@@ -257,13 +302,17 @@ function _lightAdvancedAddRGB(states, objects, entity) {
|
|
|
257
302
|
const blue_attr = entity.context.ATTRIBUTES.find(a => a.attribute === 'blue');
|
|
258
303
|
|
|
259
304
|
const rgbGetParser = (index, entity, attr, state) => {
|
|
305
|
+
let targetAttributes = entity.attributes;
|
|
306
|
+
if (entity.state !== 'on') {
|
|
307
|
+
targetAttributes = entity.context.STATE.storedValues;
|
|
308
|
+
}
|
|
260
309
|
let val = state ? state.val || 0 : 0;
|
|
261
310
|
val = val / attr.max * 255;
|
|
262
|
-
|
|
263
|
-
if (
|
|
264
|
-
|
|
311
|
+
targetAttributes.rgb_color[index] = val;
|
|
312
|
+
if (targetAttributes.rgbw_color) {
|
|
313
|
+
targetAttributes.rgbw_color[index] = val;
|
|
265
314
|
}
|
|
266
|
-
|
|
315
|
+
targetAttributes.color_mode = states.white ? RGBW : RGB;
|
|
267
316
|
};
|
|
268
317
|
|
|
269
318
|
red_attr.getParser = rgbGetParser.bind(this, 0);
|
|
@@ -274,7 +323,12 @@ function _lightAdvancedAddRGB(states, objects, entity) {
|
|
|
274
323
|
blue_attr.max = objects[states.blue] && objects[states.blue].common && objects[states.blue].common.max || 100;
|
|
275
324
|
|
|
276
325
|
entity.attributes.supported_color_modes.push(RGB);
|
|
277
|
-
entity.attributes.rgb_color = [
|
|
326
|
+
entity.attributes.rgb_color = [null,null,null];
|
|
327
|
+
entity.context.STATE.storedValues.rgb_color = [0,0,0];
|
|
328
|
+
if (states.white) {
|
|
329
|
+
entity.attributes.rgbw_color = [null,null,null,null];
|
|
330
|
+
entity.context.STATE.storedValues.rgbw_color = [0,0,0,0];
|
|
331
|
+
}
|
|
278
332
|
}
|
|
279
333
|
}
|
|
280
334
|
|
|
@@ -387,7 +441,7 @@ async function _handleTurnOnCmd(entity, command, data, user) {
|
|
|
387
441
|
// read actual state
|
|
388
442
|
const state = await adapterData.adapter.getForeignStateAsync(entity.context.STATE.getId);
|
|
389
443
|
|
|
390
|
-
// if
|
|
444
|
+
// if the light is not ON
|
|
391
445
|
if (!state || !state.val) {
|
|
392
446
|
// turn ON:
|
|
393
447
|
await adapterData.adapter.setForeignStateAsync(entity.context.STATE.setId, command.on, false, {user});
|
|
@@ -400,7 +454,7 @@ async function _handleTurnOnCmd(entity, command, data, user) {
|
|
|
400
454
|
}
|
|
401
455
|
|
|
402
456
|
/**
|
|
403
|
-
* Extract relevant ids from type-detector control object.
|
|
457
|
+
* Extract relevant ids from the type-detector control object.
|
|
404
458
|
* Result object has optional members:
|
|
405
459
|
* state
|
|
406
460
|
* brightness
|
|
@@ -484,6 +538,7 @@ async function fillLightEntityFromStates(states, objects, entity) {
|
|
|
484
538
|
if (!entity.context.COMMANDS) {
|
|
485
539
|
entity.context.COMMANDS = [];
|
|
486
540
|
}
|
|
541
|
+
entity.context.STATE.storedValues = {};
|
|
487
542
|
|
|
488
543
|
const white_attr = entity.context.ATTRIBUTES.find(a => a.attribute === 'white');
|
|
489
544
|
if (states.white) {
|
|
@@ -493,8 +548,12 @@ async function fillLightEntityFromStates(states, objects, entity) {
|
|
|
493
548
|
entity.attributes.supported_color_modes.push(RGBW);
|
|
494
549
|
white_attr.getParser = (entity, attr, state) => {
|
|
495
550
|
const val = state ? state.val || 0 : 0;
|
|
496
|
-
|
|
497
|
-
entity.
|
|
551
|
+
let targetAttributes = entity.attributes;
|
|
552
|
+
if (entity.state !== 'on') {
|
|
553
|
+
targetAttributes = entity.context.STATE.storedValues;
|
|
554
|
+
}
|
|
555
|
+
targetAttributes.rgbw_color[3] = val / attr.max * 255;
|
|
556
|
+
targetAttributes.color_mode = RGBW;
|
|
498
557
|
};
|
|
499
558
|
}
|
|
500
559
|
}
|
|
@@ -505,7 +564,7 @@ async function fillLightEntityFromStates(states, objects, entity) {
|
|
|
505
564
|
//fill in color temperature stuff.
|
|
506
565
|
await _lightAdvancedAddColorTemperature(states, objects, entity);
|
|
507
566
|
|
|
508
|
-
//if there is a "BRIGHTNESS" control, use it to dim
|
|
567
|
+
//if there is a "BRIGHTNESS" control, use it to dim the light.
|
|
509
568
|
await _lightAdvancedAddBrightness(states, objects, entity);
|
|
510
569
|
|
|
511
570
|
//add hue and sat:
|
|
@@ -523,7 +582,11 @@ async function fillLightEntityFromStates(states, objects, entity) {
|
|
|
523
582
|
entity.attributes.effect_list = Object.values(effect_attr.states);
|
|
524
583
|
effect_attr.getParser = (entity, attr, state) => {
|
|
525
584
|
state = state || {val: 0};
|
|
526
|
-
|
|
585
|
+
let targetAttributes = entity.attributes;
|
|
586
|
+
if (entity.state !== 'on') {
|
|
587
|
+
targetAttributes = entity.context.STATE.storedValues;
|
|
588
|
+
}
|
|
589
|
+
targetAttributes.effect = effect_attr.states[state.val];
|
|
527
590
|
};
|
|
528
591
|
entity.supported_features |= SUPPORT_EFFECT;
|
|
529
592
|
}
|
|
@@ -559,7 +622,7 @@ async function fillLightEntityFromStates(states, objects, entity) {
|
|
|
559
622
|
|
|
560
623
|
/**
|
|
561
624
|
* Create manual light entity.
|
|
562
|
-
* @param id - id of "main" object, i.e
|
|
625
|
+
* @param id - id of "main" object, i.e., state.
|
|
563
626
|
* @param obj - iobroker object of id param
|
|
564
627
|
* @param entity - already created entity
|
|
565
628
|
* @param objects - id object cache
|