homebridge-dummy 1.3.2 → 1.4.0-alpha.0

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.
Files changed (79) hide show
  1. package/CHANGELOG.md +12 -3
  2. package/README.md +74 -24
  3. package/config.schema.json +258 -44
  4. package/dist/accessory/base.d.ts +28 -8
  5. package/dist/accessory/base.js +53 -37
  6. package/dist/accessory/base.js.map +1 -1
  7. package/dist/accessory/group.d.ts +9 -1
  8. package/dist/accessory/group.js +14 -9
  9. package/dist/accessory/group.js.map +1 -1
  10. package/dist/accessory/helpers.d.ts +3 -5
  11. package/dist/accessory/helpers.js +12 -12
  12. package/dist/accessory/helpers.js.map +1 -1
  13. package/dist/accessory/lock.d.ts +6 -6
  14. package/dist/accessory/lock.js +14 -8
  15. package/dist/accessory/lock.js.map +1 -1
  16. package/dist/accessory/onoff/lightbulb.d.ts +9 -4
  17. package/dist/accessory/onoff/lightbulb.js +31 -4
  18. package/dist/accessory/onoff/lightbulb.js.map +1 -1
  19. package/dist/accessory/onoff/onoff.d.ts +8 -8
  20. package/dist/accessory/onoff/onoff.js +25 -13
  21. package/dist/accessory/onoff/onoff.js.map +1 -1
  22. package/dist/accessory/position/garage.d.ts +3 -4
  23. package/dist/accessory/position/garage.js +3 -3
  24. package/dist/accessory/position/garage.js.map +1 -1
  25. package/dist/accessory/position/position.d.ts +6 -6
  26. package/dist/accessory/position/position.js +14 -8
  27. package/dist/accessory/position/position.js.map +1 -1
  28. package/dist/accessory/sensor.d.ts +4 -5
  29. package/dist/accessory/sensor.js +14 -15
  30. package/dist/accessory/sensor.js.map +1 -1
  31. package/dist/accessory/thermostat.d.ts +5 -6
  32. package/dist/accessory/thermostat.js +25 -19
  33. package/dist/accessory/thermostat.js.map +1 -1
  34. package/dist/homebridge/platform.d.ts +3 -2
  35. package/dist/homebridge/platform.js +31 -12
  36. package/dist/homebridge/platform.js.map +1 -1
  37. package/dist/homebridge-ui/public/index.html +1 -1
  38. package/dist/homebridge-ui/public/ui.js +1 -1
  39. package/dist/i18n/de.d.ts +35 -0
  40. package/dist/i18n/en.d.ts +35 -0
  41. package/dist/i18n/en.js +43 -8
  42. package/dist/i18n/en.js.map +1 -1
  43. package/dist/i18n/es.d.ts +35 -0
  44. package/dist/i18n/i18n.d.ts +35 -0
  45. package/dist/i18n/ru.d.ts +35 -0
  46. package/dist/i18n/template.d.ts +35 -0
  47. package/dist/model/conditions.d.ts +17 -0
  48. package/dist/model/conditions.js +150 -0
  49. package/dist/model/conditions.js.map +1 -0
  50. package/dist/model/enums.d.ts +19 -4
  51. package/dist/model/enums.js +41 -12
  52. package/dist/model/enums.js.map +1 -1
  53. package/dist/model/types.d.ts +18 -3
  54. package/dist/model/webhook.js +23 -19
  55. package/dist/model/webhook.js.map +1 -1
  56. package/dist/timeout/fader.d.ts +9 -0
  57. package/dist/timeout/fader.js +33 -0
  58. package/dist/timeout/fader.js.map +1 -0
  59. package/dist/timeout/limiter.d.ts +2 -2
  60. package/dist/timeout/limiter.js +8 -8
  61. package/dist/timeout/limiter.js.map +1 -1
  62. package/dist/timeout/schedule.d.ts +3 -3
  63. package/dist/timeout/schedule.js +18 -18
  64. package/dist/timeout/schedule.js.map +1 -1
  65. package/dist/timeout/timeout.d.ts +6 -4
  66. package/dist/timeout/timeout.js +12 -7
  67. package/dist/timeout/timeout.js.map +1 -1
  68. package/dist/timeout/timer.d.ts +3 -4
  69. package/dist/timeout/timer.js +8 -9
  70. package/dist/timeout/timer.js.map +1 -1
  71. package/dist/tools/configMigration.js +2 -2
  72. package/dist/tools/configMigration.js.map +1 -1
  73. package/dist/tools/logWatcher.d.ts +13 -0
  74. package/dist/tools/logWatcher.js +52 -0
  75. package/dist/tools/logWatcher.js.map +1 -0
  76. package/dist/tools/primitive.d.ts +3 -0
  77. package/dist/tools/primitive.js +24 -0
  78. package/dist/tools/primitive.js.map +1 -0
  79. package/package.json +13 -3
package/CHANGELOG.md CHANGED
@@ -2,13 +2,22 @@
2
2
 
3
3
  All notable changes to homebridge-dummy will be documented in this file.
4
4
 
5
- ## 1.3.2 (2025-10-24)
5
+ ## 1.4.0-beta.4 (2025-10-26)
6
6
 
7
7
  ### ‼️ WARNING ‼️ — If upgrading from v0.9.2 or earlier, [READ THIS FIRST](https://github.com/mpatfield/homebridge-dummy?tab=readme-ov-file#v10-migration)
8
8
 
9
- ### ⚠️ Upcoming Breaking Change
9
+ ### Added
10
+ - [Trigger Conditions](https://github.com/mpatfield/homebridge-dummy?tab=readme-ov-file#trigger-conditions) to change the state of an accessory based on state changes of other Homebridge Dummy accessories or keywords in the Homebridge log
11
+ - ⚠️ Config UI for conditions is highly experimental. Please [open a ticket](https://github.com/mpatfield/homebridge-dummy/issues/new/choose) if you see any unusal behavior.
12
+ - `GET` requests for [Webhooks](https://github.com/mpatfield/homebridge-dummy#webhooks) (previously only `POST`)
13
+ - Fade Out option for `Lightbulb` brightness to emulate a simple "count-down"
14
+ - Min/max temperature settings for `Thermostat`
15
+
16
+ ### Changed
17
+ - `defaultOn` has been deprecated in favor of `defaultState` for `Lightbulb`, `Outlet`, and `Switch`
18
+ - This is backwards compatible so no manual edits are necessary
10
19
 
11
- I plan to remove `Thermostat` as a supported accessory type from a future version. If you use `Thermostat`, please add your use case to [this ticket](https://github.com/mpatfield/homebridge-dummy/issues/207) and I will help you find an alternative.
20
+ ## 1.3.2 (2025-10-24)
12
21
 
13
22
  ### Added
14
23
  - Support for `GarageDoorOpener`
package/README.md CHANGED
@@ -123,13 +123,26 @@ Using the Homebridge Config UI is the easiest way to set up this plugin. However
123
123
  "units": "MILLISECONDS | SECONDS | MINUTES | HOURS",
124
124
  "period": "HOUR | DAY | WEEK | MONTH",
125
125
  },
126
+ "conditions": {
127
+ "operator": "and | or",
128
+ "operands" [
129
+ {
130
+ "accessoryId": "string",
131
+ "accessoryState": "on | off | open | closed | locked | unlocked",
132
+ }
133
+
134
+ ]
135
+ },
126
136
  "temperatureUnits": "C" | "F",
127
- "defaultOn": true | false,
137
+ "defaultState": "on" | "off",
128
138
  "defaultBrightness": 0-100,
139
+ "fadeOut": true | false,
129
140
  "defaultLockState": "locked" | "unlocked",
130
141
  "defaultPosition": "open" | "closed",
131
142
  "defaultThermostatState": "auto" | "heat" | "cool" | "off",
132
143
  "defaultTemperature": number,
144
+ "minimumTemperature": number,
145
+ "maximumTemperature": number,
133
146
  "commandOn": "string",
134
147
  "commandOff": "string",
135
148
  "commandLock": "string",
@@ -221,14 +234,49 @@ Execute arbitrary commands (e.g. curl) when the accessory changes state
221
234
  - `unlockCommand` - Arbitrary command to execute when lock mechanism is unlocked
222
235
  - `commandTemperature` - Arbitrary command to execute when temperature changes
223
236
 
237
+ ### Trigger Conditions
238
+
239
+ You can trigger an accessory whenever a set of conditions are satisfied, for example, when other Homebridge Dummy accessories turn on.
240
+
241
+ There are two logical operators to trigger the target accessory when all (`AND`) or any (`OR`) of a set of conditions are satisfied. This is set using `operator`.
242
+
243
+ You can have an arbitrarily long list of conditions and they are checked in order.
244
+
245
+ If target accessory is not setup to auto-reset with a timer, then it will immediately return to it's default setting as soon as the conditions are no longer met.
246
+
247
+ Note that due to limitations of HomeKit and Homebridge, it is only possible to check the states of other Homebridge Dummy accessories.
248
+
249
+ One workaround is to use the `LOG` operand type which will watch the Homebridge log for the specified string or regex.
250
+
251
+ `LOG` based conditions are stateless triggers. If it is the only condition or the conditions `operator` is `OR`, then it will fire immediately. If there are other `AND` conditions, then it will not fire unless all other conditions are satisfied.
252
+
253
+ For example, if I have `LOG` condition "A" and `ACCESSORY` condition "B" for when "B" is turned "On", then if "B" is "Off"" and the pattern is found in the log, "A" will not trigger.
254
+
255
+ Note that `LOG` triggers are not instantenous and may take several seconds to fire.
256
+
257
+ Another workaround for non-Dummy accessories is to set up duplicate accessories in Homebridge Dummy and use Automation to mirror the states.
258
+
259
+ For example, if I have a physical door lock I want to "watch", then I can setup a `LockMechanism` accessory in Homebridge Dummy and create two automations to change the state of my dummy lock whenever the physical door lock is unlocked or locked.
260
+
261
+ #### Conditions Object
262
+ - `operator` - "and" to require ALL conditions to be satisfied, and "or" ANY
263
+ - `operands` - A list of accessories to "watch" for state changes to see if conditions are satisfied
264
+
265
+ #### Operand Object
266
+ - `accessoryId` - The id of the accessory to watch for state changes
267
+ - `accessoryState` - The desired accessory state to make this condition "true"
268
+
224
269
  ### Defaults
225
270
  - `temperatureUnits` - Units to use for thermostats, either 'C' or 'F'
226
- - `defaultOn` — Initial value. Default _ON_ = true, default _OFF_ = false
271
+ - `defaultState` — Initial value, either "on" or "off"
227
272
  - `defaultBrightness` — If set, lightbulb will have additional dimmer settings with this default brightness percentage
273
+ - `fadeOut` - Fade smoothly instead of abruptly from 100% to off. Requires `defaultBrightness` and `timer` to be defined.
228
274
  - `defaultLockState` - The initial value for the lock, "locked" or "unlocked"
229
275
  - `defaultPosition` — Initial position for the door/garage/window/blinds, "open" or "closed"
230
276
  - `defaultThermostatState` - The initial state for the thermostat, "auto", "heat", "cool", or "off"
231
277
  - `defaultTemperature` - The default temperature for the thermostat in `temperatureUnits` defined above
278
+ - `minimumTemperature` - Defines a minimum temperature
279
+ - `maximumTemperature` - Defines a maximum temperature
232
280
 
233
281
  ### Options
234
282
  - `enableWebook` - Turn on webhooks for this accessory. See [Webhooks](https://github.com/mpatfield/homebridge-dummy#webhooks) section below for details.
@@ -241,23 +289,7 @@ You can optionally enable webhooks on an accessory by choosing `Enable Webhooks`
241
289
 
242
290
  If at least one accessory has webhooks enabled, then Homebridge Dummy will start a webhook server on startup. The default port is `63743`, e.g. `http://localhost:63743/`. To change the port, add `webhookPort` to the top level Homebridge Dummy config (see above).
243
291
 
244
- Incoming requests must be valid JSON and include the id of the accessory, the desired command, and the value to set.
245
-
246
- For example, to turn a switch on the JSON request should look like this:
247
-
248
- ```json
249
- {
250
- "id": "17a62a7b",
251
- "command": "On",
252
- "value": true
253
- }
254
- ```
255
-
256
- Here's how you would call it from the command line.
257
-
258
- ```
259
- curl -X POST http://localhost:63743/ -H "Content-Type: application/json" -d '{"id": "17a62a7b", "command": "On", "value": true}
260
- ```
292
+ Incoming requests must include the `id` of the accessory, the desired `command`, and the `value` to set.
261
293
 
262
294
  The accessory `id` can be found in the plugin JSON config.
263
295
 
@@ -269,18 +301,34 @@ Here are the possible values for `command` and their respective valid `value`
269
301
  - `TargetHeatingCoolingState` - 0 (OFF), 1 (HEAT), 2 (COOL), 3 (AUTO)
270
302
  - `TargetPosition` - number from 0-100
271
303
  - `TargetTemperature` - number between 10°C and 38°C
304
+ - For `TargetTemperature` you may optionally supply a `unit` (either 'F' or 'C') to allow you to pass in Fahrenheit or Celsius units.
305
+
306
+ #### GET Example
307
+
308
+ ```
309
+ http://localhost:63743/?id=ACCESSORY_ID&command=On&value=true
310
+ ```
272
311
 
273
- For `TargetTemperature` you may optionally supply a `unit` (either 'F' or 'C') to allow you to pass in Fahrenheit or Celsius units.
312
+ ### POST Example
313
+
314
+ POST requrests must be valid JSON.
315
+
316
+ For example, to turn a switch on the JSON request should look like this:
274
317
 
275
318
  ```json
276
319
  {
277
- "id": "18a35b6c",
278
- "command": "TargetTemperature",
279
- "value": 72,
280
- "unit": "F"
320
+ "id": "ACCESSORY_ID",
321
+ "command": "On",
322
+ "value": true
281
323
  }
282
324
  ```
283
325
 
326
+ Here's how you would call it from the command line.
327
+
328
+ ```
329
+ curl -X POST http://localhost:63743/ -H "Content-Type: application/json" -d '{"id": "ACCESSORY_ID", "command": "On", "value": true}
330
+ ```
331
+
284
332
  ## Credits
285
333
 
286
334
  [@jotzet79](https://github.com/sponsors/jotzet79) for German translations
@@ -297,6 +345,8 @@ Sensor feature inspired by [Homebridge-Delay-Switch](https://github.com/nitaybz/
297
345
 
298
346
  Command feature inspired by [homebridge-cmdtrigger](https://github.com/hallos/homebridge-cmdtrigger) by [@hallos](https://github.com/sponsors/hallos)
299
347
 
348
+ Log watch trigger feature inspired by [hb-virtual-switch](https://github.com/Plankske/hb-virtual-switch/) by [@Plankske](https://github.com/sponsors/Plankske)
349
+
300
350
  Special thanks to [@nfarina](https://github.com/sponsors/nfarina) for creating the original version of this plugin and maintaining it for almost 10 (!!!) years
301
351
 
302
352
  And to the amazing creators/contributors of [Homebridge](https://homebridge.io) who made this plugin possible!
@@ -37,7 +37,10 @@
37
37
  "title": "${config.title.delay}",
38
38
  "minimum": 1
39
39
  },
40
- "units": { "$ref": "#/definitions/units" },
40
+ "units": {
41
+ "$ref": "#/definitions/units",
42
+ "required": true
43
+ },
41
44
  "random": {
42
45
  "type": "boolean",
43
46
  "title": "${config.title.random}",
@@ -158,13 +161,15 @@
158
161
  "type": "string",
159
162
  "title": "${config.title.units}",
160
163
  "enum": [ "SECONDS", "MINUTES", "HOURS"],
161
- "enumNames": ["${config.enumNames.seconds}", "${config.enumNames.minutes}", "${config.enumNames.hours}"]
164
+ "enumNames": ["${config.enumNames.seconds}", "${config.enumNames.minutes}", "${config.enumNames.hours}"],
165
+ "required": true
162
166
  },
163
167
  "period": {
164
168
  "type": "string",
165
169
  "title": "${config.title.period}",
166
170
  "enum": [ "HOUR", "DAY", "WEEK", "MONTH"],
167
- "enumNames": ["${config.enumNames.hour}", "${config.enumNames.day}", "${config.enumNames.week}", "${config.enumNames.month}"]
171
+ "enumNames": ["${config.enumNames.hour}", "${config.enumNames.day}", "${config.enumNames.week}", "${config.enumNames.month}"],
172
+ "required": true
168
173
  }
169
174
  },
170
175
  "allOf": [
@@ -205,6 +210,90 @@
205
210
  }
206
211
  }
207
212
  ]
213
+ },
214
+ "operand": {
215
+ "type": "object",
216
+ "properties": {
217
+ "type": {
218
+ "type": "string",
219
+ "title": "${config.title.type}",
220
+ "enum": [ "ACCESSORY", "LOG" ],
221
+ "enumNames": [ "${config.enumNames.accessory}", "${config.enumNames.log}" ],
222
+ "required": true
223
+ },
224
+ "accessoryId": {
225
+ "type": "string",
226
+ "title": "${config.title.accessory}"
227
+ },
228
+ "accessoryState": {
229
+ "type": "string",
230
+ "title": "${config.title.accessoryState}",
231
+ "enum": [ "on", "off", "open", "closed", "locked", "unlocked" ],
232
+ "enumNames": [ "${config.enumNames.on}", "${config.enumNames.off}", "${config.enumNames.open}", "${config.enumNames.closed}", "${config.enumNames.secured}", "${config.enumNames.unsecured}" ]
233
+ },
234
+ "pattern": {
235
+ "type": "string",
236
+ "title": "${config.title.pattern}"
237
+ }
238
+ },
239
+ "allOf": [
240
+ {
241
+ "if": {
242
+ "properties": {
243
+ "type": {
244
+ "const": "ACCESSORY"
245
+ }
246
+ },
247
+ "required": ["type"]
248
+ },
249
+ "then": {
250
+ "required": ["accessoryId", "accessoryState"]
251
+ }
252
+ },
253
+ {
254
+ "if": {
255
+ "properties": {
256
+ "type": {
257
+ "const": "LOG"
258
+ }
259
+ },
260
+ "required": ["type"]
261
+ },
262
+ "then": {
263
+ "required": ["pattern"]
264
+ }
265
+ }
266
+ ]
267
+ },
268
+ "conditions": {
269
+ "type": "object",
270
+ "properties": {
271
+ "operator": {
272
+ "type": "string",
273
+ "title": "${config.title.operator}",
274
+ "enum": [ "and", "or"],
275
+ "enumNames": ["${config.enumNames.operatorAnd}", "${config.enumNames.operatorOr}"]
276
+ },
277
+ "operands": {
278
+ "type": "array",
279
+ "minItems":1,
280
+ "items": { "$ref": "#/definitions/operand" }
281
+ }
282
+ },
283
+ "allOf": [
284
+ {
285
+ "if": {
286
+ "properties": {
287
+ "operator": {
288
+ }
289
+ },
290
+ "required": ["operator"]
291
+ },
292
+ "then": {
293
+ "required": ["operands"]
294
+ }
295
+ }
296
+ ]
208
297
  }
209
298
  },
210
299
  "properties": {
@@ -239,9 +328,10 @@
239
328
  "timer": { "$ref": "#/definitions/timer" },
240
329
  "schedule": { "$ref": "#/definitions/schedule" },
241
330
  "limiter": { "$ref": "#/definitions/limiter" },
331
+ "conditions": { "$ref": "#/definitions/conditions" },
242
332
  "temperatureUnits": {
243
333
  "type": "string",
244
- "title": "${config.title.units}",
334
+ "title": "${config.title.temperatureUnits}",
245
335
  "enum": ["C", "F"],
246
336
  "enumNames": ["${config.enumNames.celsius}", "${config.enumNames.fahrenheit}"]
247
337
  },
@@ -251,10 +341,15 @@
251
341
  "minimum": 0,
252
342
  "maximum": 100
253
343
  },
254
- "defaultOn": {
255
- "type": "number",
344
+ "fadeOut": {
345
+ "type": "boolean",
346
+ "title": "${config.title.fadeOut}",
347
+ "description": "${config.description.fadeOut}"
348
+ },
349
+ "defaultState": {
350
+ "type": "string",
256
351
  "title": "${config.title.defaultState}",
257
- "enum": [1, 0],
352
+ "enum": ["on", "off"],
258
353
  "enumNames": ["${config.enumNames.on}", "${config.enumNames.off}"]
259
354
  },
260
355
  "defaultLockState": {
@@ -279,6 +374,14 @@
279
374
  "type": "number",
280
375
  "title": "${config.title.defaultTemperature}"
281
376
  },
377
+ "minimumTemperature": {
378
+ "type": "number",
379
+ "title": "${config.title.minimumTemperature}"
380
+ },
381
+ "maximumTemperature": {
382
+ "type": "number",
383
+ "title": "${config.title.maximumTemperature}"
384
+ },
282
385
  "commandOn": {
283
386
  "type": "string",
284
387
  "title": "${config.title.commandOn}"
@@ -348,15 +451,15 @@
348
451
  "items": [
349
452
  {
350
453
  "key": "accessories[].name",
351
- "flex": "1 1 33%"
454
+ "flex": "0 0 33%"
352
455
  },
353
456
  {
354
457
  "key": "accessories[].type",
355
- "flex": "1 1 33%"
458
+ "flex": "0 0 33%"
356
459
  },
357
460
  {
358
461
  "key": "accessories[].groupName",
359
- "flex": "1 1 33%"
462
+ "flex": "0 0 33%"
360
463
  }
361
464
  ]
362
465
  },
@@ -364,7 +467,7 @@
364
467
  "type": "fieldset",
365
468
  "notitle": true,
366
469
  "condition": {
367
- "functionBody": "return model.accessories?.[arguments[1]]?.type !== undefined;"
470
+ "functionBody": "return model.accessories?.[arguments[1]]?.type;"
368
471
  },
369
472
  "items": [
370
473
  {
@@ -376,7 +479,7 @@
376
479
  },
377
480
  "items": [
378
481
  {
379
- "key": "accessories[].defaultOn",
482
+ "key": "accessories[].defaultState",
380
483
  "flex": "0 0 33%"
381
484
  },
382
485
  {
@@ -387,7 +490,7 @@
387
490
  "key": "accessories[].defaultBrightness",
388
491
  "flex": "0 0 33%",
389
492
  "condition": {
390
- "functionBody": "return ['Lightbulb'].includes(model.accessories?.[arguments[1]]?.type);"
493
+ "functionBody": "return model.accessories?.[arguments[1]]?.type === 'Lightbulb';"
391
494
  }
392
495
  }
393
496
  ]
@@ -436,16 +539,34 @@
436
539
  "functionBody": "return ['Thermostat'].includes(model.accessories?.[arguments[1]]?.type);"
437
540
  },
438
541
  "items": [
542
+ {
543
+ "key": "accessories[].defaultThermostatState",
544
+ "flex": "0 0 33%"
545
+ },
439
546
  {
440
547
  "key": "accessories[].temperatureUnits",
441
548
  "flex": "0 0 33%"
549
+ }
550
+ ]
551
+ },
552
+ {
553
+ "type": "div",
554
+ "displayFlex": true,
555
+ "flex-direction": "row",
556
+ "condition": {
557
+ "functionBody": "return ['Thermostat'].includes(model.accessories?.[arguments[1]]?.type);"
558
+ },
559
+ "items": [
560
+ {
561
+ "key": "accessories[].defaultTemperature",
562
+ "flex": "0 0 33%"
442
563
  },
443
564
  {
444
- "key": "accessories[].defaultThermostatState",
565
+ "key": "accessories[].minimumTemperature",
445
566
  "flex": "0 0 33%"
446
567
  },
447
568
  {
448
- "key": "accessories[].defaultTemperature",
569
+ "key": "accessories[].maximumTemperature",
449
570
  "flex": "0 0 33%"
450
571
  }
451
572
  ]
@@ -479,7 +600,7 @@
479
600
  "title": "${config.title.schedule}",
480
601
  "description": "${config.description.schedule}",
481
602
  "condition": {
482
- "functionBody": "const type = model.accessories?.[arguments[1]]?.type; return type !== undefined && type !== 'Thermostat';"
603
+ "functionBody": "const type = model.accessories?.[arguments[1]]?.type; return type && type !== 'Thermostat';"
483
604
  },
484
605
  "items": [
485
606
  {
@@ -489,39 +610,44 @@
489
610
  "items": [
490
611
  {
491
612
  "key": "accessories[].schedule.type",
492
- "flex": "1 1 33%"
613
+ "flex": "0 0 auto"
614
+ },
615
+ {
616
+ "type": "div",
617
+ "flex": "0 0 5px"
493
618
  },
494
619
  {
495
620
  "key": "accessories[].schedule.interval",
496
- "flex": "1 1 33%",
621
+ "flex": "0 0 auto",
497
622
  "condition": {
498
623
  "functionBody": "return model.accessories?.[arguments[1]]?.schedule?.type === 'INTERVAL';"
499
624
  }
500
625
  },
626
+ {
627
+ "type": "div",
628
+ "flex": "0 0 5px"
629
+ },
501
630
  {
502
631
  "key": "accessories[].schedule.units",
503
- "flex": "1 1 33%",
632
+ "flex": "0 0 auto",
504
633
  "condition": {
505
- "functionBody": "return model.accessories?.[arguments[1]]?.schedule?.type === 'INTERVAL';"
634
+ "functionBody": "return model.accessories?.[arguments[1]]?.schedule?.interval !== undefined;"
506
635
  }
507
- }
508
- ]
509
- },
510
- {
511
- "type": "div",
512
- "displayFlex": true,
513
- "flex-direction": "row",
514
- "condition": {
515
- "functionBody": "return model.accessories?.[arguments[1]]?.schedule?.type === 'CRON';"
516
- },
517
- "items": [
636
+ },
518
637
  {
519
638
  "key": "accessories[].schedule.cron",
520
- "flex": "1 1 33%"
639
+ "flex": "0 0 auto",
640
+ "condition": {
641
+ "functionBody": "return model.accessories?.[arguments[1]]?.schedule?.type === 'CRON';"
642
+ }
643
+ },
644
+ {
645
+ "type": "div",
646
+ "flex": "0 0 5px"
521
647
  },
522
648
  {
523
649
  "key": "accessories[].schedule.cronCustom",
524
- "flex": "1 1 67%",
650
+ "flex": "1 0 auto",
525
651
  "condition": {
526
652
  "functionBody": "return model.accessories?.[arguments[1]]?.schedule?.cron === 'CRON_CUSTOM';"
527
653
  }
@@ -531,7 +657,7 @@
531
657
  {
532
658
  "key": "accessories[].schedule.random",
533
659
  "condition": {
534
- "functionBody": "const schedule = model.accessories?.[arguments[1]]?.schedule; return schedule?.type === 'INTERVAL' && schedule?.interval !== undefined;"
660
+ "functionBody": "const schedule = model.accessories?.[arguments[1]]?.schedule; return schedule?.type === 'INTERVAL' && schedule?.interval;"
535
661
  }
536
662
  }
537
663
  ]
@@ -541,7 +667,7 @@
541
667
  "title": "${config.title.timer}",
542
668
  "description": "${config.description.timer}",
543
669
  "condition": {
544
- "functionBody": "const type = model.accessories?.[arguments[1]]?.type; return type !== undefined && type !== 'Thermostat';"
670
+ "functionBody": "const type = model.accessories?.[arguments[1]]?.type; return type && type !== 'Thermostat';"
545
671
  },
546
672
  "items": [
547
673
  {
@@ -551,11 +677,14 @@
551
677
  "items": [
552
678
  {
553
679
  "key": "accessories[].timer.delay",
554
- "flex": "1 1 33%"
680
+ "flex": "0 0 33%"
555
681
  },
556
682
  {
557
683
  "key": "accessories[].timer.units",
558
- "flex": "1 1 33%"
684
+ "flex": "0 0 33%",
685
+ "condition": {
686
+ "functionBody": "return model.accessories?.[arguments[1]]?.timer?.delay;"
687
+ }
559
688
  }
560
689
  ]
561
690
  },
@@ -563,14 +692,21 @@
563
692
  "type": "fieldset",
564
693
  "notitle": true,
565
694
  "condition": {
566
- "functionBody": "return model.accessories?.[arguments[1]]?.timer?.delay !== undefined;"
695
+ "functionBody": "return model.accessories?.[arguments[1]]?.timer?.delay;"
567
696
  },
568
697
  "items": [
569
698
  "accessories[].timer.random",
699
+ {
700
+ "key": "accessories[].fadeOut",
701
+ "flex": "1 0 33%",
702
+ "condition": {
703
+ "functionBody": "return model.accessories?.[arguments[1]]?.defaultBrightness !== undefined && model.accessories?.[arguments[1]]?.defaultState !== 'on' && model.accessories?.[arguments[1]]?.timer?.delay !== undefined;"
704
+ }
705
+ },
570
706
  {
571
707
  "key": "accessories[].sensor.timerControlled",
572
708
  "condition": {
573
- "functionBody": "return model.accessories?.[arguments[1]]?.sensor?.type !== undefined;"
709
+ "functionBody": "return model.accessories?.[arguments[1]]?.sensor?.type;"
574
710
  }
575
711
  }
576
712
  ]
@@ -582,7 +718,7 @@
582
718
  "title": "${config.title.limiter}",
583
719
  "description": "${config.description.limiter}",
584
720
  "condition": {
585
- "functionBody": "const type = model.accessories?.[arguments[1]]?.type; return type !== undefined && type !== 'Thermostat';"
721
+ "functionBody": "const type = model.accessories?.[arguments[1]]?.type; return type && type !== 'Thermostat';"
586
722
  },
587
723
  "items": [
588
724
  {
@@ -592,15 +728,93 @@
592
728
  "items": [
593
729
  {
594
730
  "key": "accessories[].limiter.limit",
595
- "flex": "1 1 33%"
731
+ "flex": "0 0 33%"
596
732
  },
597
733
  {
598
734
  "key": "accessories[].limiter.units",
599
- "flex": "1 1 33%"
735
+ "flex": "0 0 33%",
736
+ "condition": {
737
+ "functionBody": "return model.accessories?.[arguments[1]]?.limiter?.limit;"
738
+ }
600
739
  },
601
740
  {
602
741
  "key": "accessories[].limiter.period",
603
- "flex": "1 1 33%"
742
+ "flex": "0 0 33%",
743
+ "condition": {
744
+ "functionBody": "return model.accessories?.[arguments[1]]?.limiter?.limit;"
745
+ }
746
+ }
747
+ ]
748
+ }
749
+ ]
750
+ },
751
+ {
752
+ "type": "fieldset",
753
+ "title": "${config.title.conditions}",
754
+ "description": "${config.description.conditions}",
755
+ "condition": {
756
+ "functionBody": "const type = model.accessories?.[arguments[1]]?.type; return model.accessories?.length > 1 && type && type !== 'Thermostat'"
757
+ },
758
+ "items": [
759
+ {
760
+ "type": "div",
761
+ "displayFlex": true,
762
+ "flex-direction": "row",
763
+ "items": [
764
+ {
765
+ "key": "accessories[].conditions.operator",
766
+ "flex": "0 0 auto"
767
+ }
768
+ ]
769
+ }
770
+ ]
771
+ },
772
+ {
773
+ "type": "array",
774
+ "title": "${config.title.condition}",
775
+ "notitle": true,
776
+ "condition": {
777
+ "functionBody": "return model.accessories?.[arguments[1]]?.conditions?.operator;"
778
+ },
779
+ "key": "accessories[].conditions.operands",
780
+ "items": [
781
+ {
782
+ "type": "div",
783
+ "displayFlex": true,
784
+ "flex-direction": "row",
785
+ "items": [
786
+ {
787
+ "key": "accessories[].conditions.operands[].type",
788
+ "flex": "0 0 auto"
789
+ },
790
+ {
791
+ "type": "div",
792
+ "flex": "0 0 5px"
793
+ },
794
+ {
795
+ "key": "accessories[].conditions.operands[].accessoryId",
796
+ "flex": "0 0 auto",
797
+ "condition": {
798
+ "functionBody": "return model.accessories?.[arguments[1][0]]?.conditions?.operands?.[arguments[1][1]]?.type === 'ACCESSORY';"
799
+ }
800
+ },
801
+ {
802
+ "type": "div",
803
+ "flex": "0 0 5px"
804
+ },
805
+ {
806
+ "key": "accessories[].conditions.operands[].accessoryState",
807
+ "flex": "0 0 auto",
808
+ "condition": {
809
+ "functionBody": "return model.accessories?.[arguments[1][0]]?.conditions?.operands?.[arguments[1][1]]?.accessoryId;"
810
+ }
811
+ },
812
+ {
813
+ "key": "accessories[].conditions.operands[].pattern",
814
+ "flex": "1 0 auto",
815
+ "condition": {
816
+ "functionBody": "return model.accessories?.[arguments[1][0]]?.conditions?.operands?.[arguments[1][1]]?.type === 'LOG';"
817
+ }
604
818
  }
605
819
  ]
606
820
  }
@@ -611,7 +825,7 @@
611
825
  "title": "${config.title.commands}",
612
826
  "description": "${config.description.commands}",
613
827
  "condition": {
614
- "functionBody": "return model.accessories?.[arguments[1]]?.type !== undefined;"
828
+ "functionBody": "return model.accessories?.[arguments[1]]?.type;"
615
829
  },
616
830
  "items": [
617
831
  {