homebridge-dummy 1.3.2 → 1.4.0-beta.1
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/CHANGELOG.md +12 -1
- package/README.md +36 -2
- package/config.schema.json +153 -29
- package/dist/accessory/base.d.ts +28 -8
- package/dist/accessory/base.js +53 -37
- package/dist/accessory/base.js.map +1 -1
- package/dist/accessory/group.d.ts +9 -1
- package/dist/accessory/group.js +14 -9
- package/dist/accessory/group.js.map +1 -1
- package/dist/accessory/helpers.d.ts +3 -5
- package/dist/accessory/helpers.js +12 -12
- package/dist/accessory/helpers.js.map +1 -1
- package/dist/accessory/lock.d.ts +6 -6
- package/dist/accessory/lock.js +14 -8
- package/dist/accessory/lock.js.map +1 -1
- package/dist/accessory/onoff/lightbulb.d.ts +9 -4
- package/dist/accessory/onoff/lightbulb.js +31 -4
- package/dist/accessory/onoff/lightbulb.js.map +1 -1
- package/dist/accessory/onoff/onoff.d.ts +8 -8
- package/dist/accessory/onoff/onoff.js +25 -13
- package/dist/accessory/onoff/onoff.js.map +1 -1
- package/dist/accessory/position/garage.d.ts +3 -4
- package/dist/accessory/position/garage.js +3 -3
- package/dist/accessory/position/garage.js.map +1 -1
- package/dist/accessory/position/position.d.ts +6 -6
- package/dist/accessory/position/position.js +14 -8
- package/dist/accessory/position/position.js.map +1 -1
- package/dist/accessory/sensor.d.ts +4 -5
- package/dist/accessory/sensor.js +14 -15
- package/dist/accessory/sensor.js.map +1 -1
- package/dist/accessory/thermostat.d.ts +5 -6
- package/dist/accessory/thermostat.js +17 -17
- package/dist/accessory/thermostat.js.map +1 -1
- package/dist/homebridge/platform.d.ts +1 -1
- package/dist/homebridge/platform.js +29 -12
- package/dist/homebridge/platform.js.map +1 -1
- package/dist/homebridge-ui/public/index.html +1 -1
- package/dist/homebridge-ui/public/ui.js +1 -1
- package/dist/i18n/de.d.ts +22 -0
- package/dist/i18n/en.d.ts +22 -0
- package/dist/i18n/en.js +30 -8
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/es.d.ts +22 -0
- package/dist/i18n/i18n.d.ts +22 -0
- package/dist/i18n/ru.d.ts +22 -0
- package/dist/i18n/template.d.ts +22 -0
- package/dist/model/conditions.d.ts +14 -0
- package/dist/model/conditions.js +99 -0
- package/dist/model/conditions.js.map +1 -0
- package/dist/model/enums.d.ts +15 -4
- package/dist/model/enums.js +36 -12
- package/dist/model/enums.js.map +1 -1
- package/dist/model/types.d.ts +14 -3
- package/dist/timeout/fader.d.ts +9 -0
- package/dist/timeout/fader.js +33 -0
- package/dist/timeout/fader.js.map +1 -0
- package/dist/timeout/limiter.d.ts +2 -2
- package/dist/timeout/limiter.js +8 -8
- package/dist/timeout/limiter.js.map +1 -1
- package/dist/timeout/schedule.d.ts +3 -3
- package/dist/timeout/schedule.js +18 -18
- package/dist/timeout/schedule.js.map +1 -1
- package/dist/timeout/timeout.d.ts +6 -4
- package/dist/timeout/timeout.js +12 -7
- package/dist/timeout/timeout.js.map +1 -1
- package/dist/timeout/timer.d.ts +3 -4
- package/dist/timeout/timer.js +8 -9
- package/dist/timeout/timer.js.map +1 -1
- package/dist/tools/configMigration.js +2 -2
- package/dist/tools/configMigration.js.map +1 -1
- package/package.json +10 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to homebridge-dummy will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## 1.
|
|
5
|
+
## 1.4.0-beta.1 (2025-10-25)
|
|
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
|
|
|
@@ -10,6 +10,17 @@ All notable changes to homebridge-dummy will be documented in this file.
|
|
|
10
10
|
|
|
11
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.
|
|
12
12
|
|
|
13
|
+
### Added
|
|
14
|
+
- Trigger Conditions ([docs](https://github.com/mpatfield/homebridge-dummy?tab=readme-ov-file#trigger-conditions)) to trigger an accessory based on the state changes of other Homebridge Dummy accessories
|
|
15
|
+
- ⚠️ 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.
|
|
16
|
+
- Fade Out option for `Lightbulb` brightness to make a simple "count-down"
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
- `defaultOn` has been deprecated in favor of `defaultState` for `Lightbulb`, `Outlet`, and `Switch`
|
|
20
|
+
- This is backwards compatible so no manual edits are necessary
|
|
21
|
+
|
|
22
|
+
## 1.3.2 (2025-10-24)
|
|
23
|
+
|
|
13
24
|
### Added
|
|
14
25
|
- Support for `GarageDoorOpener`
|
|
15
26
|
- Русский перевод. Спасибо, [@Silverdragon122](https://github.com/sponsors/Silverdragon122)!
|
package/README.md
CHANGED
|
@@ -123,9 +123,20 @@ 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
|
-
"
|
|
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",
|
|
@@ -221,10 +232,33 @@ Execute arbitrary commands (e.g. curl) when the accessory changes state
|
|
|
221
232
|
- `unlockCommand` - Arbitrary command to execute when lock mechanism is unlocked
|
|
222
233
|
- `commandTemperature` - Arbitrary command to execute when temperature changes
|
|
223
234
|
|
|
235
|
+
### Trigger Conditions
|
|
236
|
+
|
|
237
|
+
You can trigger an accessory whenever a set of conditions are satisfied, for example, when other Homebridge Dummy accessories turn on.
|
|
238
|
+
|
|
239
|
+
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`.
|
|
240
|
+
|
|
241
|
+
You can have an arbitrarily long list of conditions and they are checked in order.
|
|
242
|
+
|
|
243
|
+
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.
|
|
244
|
+
|
|
245
|
+
Note that due to limitations of HomeKit and Homebridge, it is only possible to check the states of other Homebridge Dummy accessories. One possible workaround is to set up duplicate accessories in Homebridge Dummy and use Automation to mirror the states.
|
|
246
|
+
|
|
247
|
+
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.
|
|
248
|
+
|
|
249
|
+
#### Conditions Object
|
|
250
|
+
- `operator` - "and" to require ALL conditions to be satisfied, and "or" ANY
|
|
251
|
+
- `operands` - A list of accessories to "watch" for state changes to see if conditions are satisfied
|
|
252
|
+
|
|
253
|
+
#### Operand Object
|
|
254
|
+
- `accessoryId` - The id of the accessory to watch for state changes
|
|
255
|
+
- `accessoryState` - The desired accessory state to make this condition "true"
|
|
256
|
+
|
|
224
257
|
### Defaults
|
|
225
258
|
- `temperatureUnits` - Units to use for thermostats, either 'C' or 'F'
|
|
226
|
-
- `
|
|
259
|
+
- `defaultState` — Initial value, either "on" or "off"
|
|
227
260
|
- `defaultBrightness` — If set, lightbulb will have additional dimmer settings with this default brightness percentage
|
|
261
|
+
- `fadeOut` - Fade smoothly instead of abruptly from 100% to off. Requires `defaultBrightness` and `timer` to be defined.
|
|
228
262
|
- `defaultLockState` - The initial value for the lock, "locked" or "unlocked"
|
|
229
263
|
- `defaultPosition` — Initial position for the door/garage/window/blinds, "open" or "closed"
|
|
230
264
|
- `defaultThermostatState` - The initial state for the thermostat, "auto", "heat", "cool", or "off"
|
package/config.schema.json
CHANGED
|
@@ -37,7 +37,10 @@
|
|
|
37
37
|
"title": "${config.title.delay}",
|
|
38
38
|
"minimum": 1
|
|
39
39
|
},
|
|
40
|
-
"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,53 @@
|
|
|
205
210
|
}
|
|
206
211
|
}
|
|
207
212
|
]
|
|
213
|
+
},
|
|
214
|
+
"operand": {
|
|
215
|
+
"type": "object",
|
|
216
|
+
"properties": {
|
|
217
|
+
"accessoryId": {
|
|
218
|
+
"type": "string",
|
|
219
|
+
"title": "${config.title.accessory}",
|
|
220
|
+
"required": true
|
|
221
|
+
},
|
|
222
|
+
"accessoryState": {
|
|
223
|
+
"type": "string",
|
|
224
|
+
"title": "${config.title.accessoryState}",
|
|
225
|
+
"enum": [ "on", "off", "open", "closed", "locked", "unlocked" ],
|
|
226
|
+
"enumNames": [ "${config.enumNames.on}", "${config.enumNames.off}", "${config.enumNames.open}", "${config.enumNames.closed}", "${config.enumNames.secured}", "${config.enumNames.unsecured}" ],
|
|
227
|
+
"required": true
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
"conditions": {
|
|
232
|
+
"type": "object",
|
|
233
|
+
"properties": {
|
|
234
|
+
"operator": {
|
|
235
|
+
"type": "string",
|
|
236
|
+
"title": "${config.title.operator}",
|
|
237
|
+
"enum": [ "and", "or"],
|
|
238
|
+
"enumNames": ["${config.enumNames.operatorAnd}", "${config.enumNames.operatorOr}"]
|
|
239
|
+
},
|
|
240
|
+
"operands": {
|
|
241
|
+
"type": "array",
|
|
242
|
+
"minItems":1,
|
|
243
|
+
"items": { "$ref": "#/definitions/operand" }
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
"allOf": [
|
|
247
|
+
{
|
|
248
|
+
"if": {
|
|
249
|
+
"properties": {
|
|
250
|
+
"operator": {
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
"required": ["operator"]
|
|
254
|
+
},
|
|
255
|
+
"then": {
|
|
256
|
+
"required": ["operands"]
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
]
|
|
208
260
|
}
|
|
209
261
|
},
|
|
210
262
|
"properties": {
|
|
@@ -239,6 +291,7 @@
|
|
|
239
291
|
"timer": { "$ref": "#/definitions/timer" },
|
|
240
292
|
"schedule": { "$ref": "#/definitions/schedule" },
|
|
241
293
|
"limiter": { "$ref": "#/definitions/limiter" },
|
|
294
|
+
"conditions": { "$ref": "#/definitions/conditions" },
|
|
242
295
|
"temperatureUnits": {
|
|
243
296
|
"type": "string",
|
|
244
297
|
"title": "${config.title.units}",
|
|
@@ -251,10 +304,15 @@
|
|
|
251
304
|
"minimum": 0,
|
|
252
305
|
"maximum": 100
|
|
253
306
|
},
|
|
254
|
-
"
|
|
255
|
-
"type": "
|
|
307
|
+
"fadeOut": {
|
|
308
|
+
"type": "boolean",
|
|
309
|
+
"title": "${config.title.fadeOut}",
|
|
310
|
+
"description": "${config.description.fadeOut}"
|
|
311
|
+
},
|
|
312
|
+
"defaultState": {
|
|
313
|
+
"type": "string",
|
|
256
314
|
"title": "${config.title.defaultState}",
|
|
257
|
-
"enum": [
|
|
315
|
+
"enum": ["on", "off"],
|
|
258
316
|
"enumNames": ["${config.enumNames.on}", "${config.enumNames.off}"]
|
|
259
317
|
},
|
|
260
318
|
"defaultLockState": {
|
|
@@ -348,15 +406,15 @@
|
|
|
348
406
|
"items": [
|
|
349
407
|
{
|
|
350
408
|
"key": "accessories[].name",
|
|
351
|
-
"flex": "
|
|
409
|
+
"flex": "0 0 33%"
|
|
352
410
|
},
|
|
353
411
|
{
|
|
354
412
|
"key": "accessories[].type",
|
|
355
|
-
"flex": "
|
|
413
|
+
"flex": "0 0 33%"
|
|
356
414
|
},
|
|
357
415
|
{
|
|
358
416
|
"key": "accessories[].groupName",
|
|
359
|
-
"flex": "
|
|
417
|
+
"flex": "0 0 33%"
|
|
360
418
|
}
|
|
361
419
|
]
|
|
362
420
|
},
|
|
@@ -364,7 +422,7 @@
|
|
|
364
422
|
"type": "fieldset",
|
|
365
423
|
"notitle": true,
|
|
366
424
|
"condition": {
|
|
367
|
-
"functionBody": "return model.accessories?.[arguments[1]]?.type
|
|
425
|
+
"functionBody": "return model.accessories?.[arguments[1]]?.type;"
|
|
368
426
|
},
|
|
369
427
|
"items": [
|
|
370
428
|
{
|
|
@@ -376,7 +434,7 @@
|
|
|
376
434
|
},
|
|
377
435
|
"items": [
|
|
378
436
|
{
|
|
379
|
-
"key": "accessories[].
|
|
437
|
+
"key": "accessories[].defaultState",
|
|
380
438
|
"flex": "0 0 33%"
|
|
381
439
|
},
|
|
382
440
|
{
|
|
@@ -387,7 +445,7 @@
|
|
|
387
445
|
"key": "accessories[].defaultBrightness",
|
|
388
446
|
"flex": "0 0 33%",
|
|
389
447
|
"condition": {
|
|
390
|
-
"functionBody": "return
|
|
448
|
+
"functionBody": "return model.accessories?.[arguments[1]]?.type === 'Lightbulb';"
|
|
391
449
|
}
|
|
392
450
|
}
|
|
393
451
|
]
|
|
@@ -479,7 +537,7 @@
|
|
|
479
537
|
"title": "${config.title.schedule}",
|
|
480
538
|
"description": "${config.description.schedule}",
|
|
481
539
|
"condition": {
|
|
482
|
-
"functionBody": "const type = model.accessories?.[arguments[1]]?.type; return type
|
|
540
|
+
"functionBody": "const type = model.accessories?.[arguments[1]]?.type; return type && type !== 'Thermostat';"
|
|
483
541
|
},
|
|
484
542
|
"items": [
|
|
485
543
|
{
|
|
@@ -489,18 +547,18 @@
|
|
|
489
547
|
"items": [
|
|
490
548
|
{
|
|
491
549
|
"key": "accessories[].schedule.type",
|
|
492
|
-
"flex": "
|
|
550
|
+
"flex": "0 0 33%"
|
|
493
551
|
},
|
|
494
552
|
{
|
|
495
553
|
"key": "accessories[].schedule.interval",
|
|
496
|
-
"flex": "
|
|
554
|
+
"flex": "0 0 33%",
|
|
497
555
|
"condition": {
|
|
498
556
|
"functionBody": "return model.accessories?.[arguments[1]]?.schedule?.type === 'INTERVAL';"
|
|
499
557
|
}
|
|
500
558
|
},
|
|
501
559
|
{
|
|
502
560
|
"key": "accessories[].schedule.units",
|
|
503
|
-
"flex": "
|
|
561
|
+
"flex": "0 0 33%",
|
|
504
562
|
"condition": {
|
|
505
563
|
"functionBody": "return model.accessories?.[arguments[1]]?.schedule?.type === 'INTERVAL';"
|
|
506
564
|
}
|
|
@@ -517,11 +575,11 @@
|
|
|
517
575
|
"items": [
|
|
518
576
|
{
|
|
519
577
|
"key": "accessories[].schedule.cron",
|
|
520
|
-
"flex": "
|
|
578
|
+
"flex": "0 0 33%"
|
|
521
579
|
},
|
|
522
580
|
{
|
|
523
581
|
"key": "accessories[].schedule.cronCustom",
|
|
524
|
-
"flex": "
|
|
582
|
+
"flex": "0 0 67%",
|
|
525
583
|
"condition": {
|
|
526
584
|
"functionBody": "return model.accessories?.[arguments[1]]?.schedule?.cron === 'CRON_CUSTOM';"
|
|
527
585
|
}
|
|
@@ -531,7 +589,7 @@
|
|
|
531
589
|
{
|
|
532
590
|
"key": "accessories[].schedule.random",
|
|
533
591
|
"condition": {
|
|
534
|
-
"functionBody": "const schedule = model.accessories?.[arguments[1]]?.schedule; return schedule?.type === 'INTERVAL' && schedule?.interval
|
|
592
|
+
"functionBody": "const schedule = model.accessories?.[arguments[1]]?.schedule; return schedule?.type === 'INTERVAL' && schedule?.interval;"
|
|
535
593
|
}
|
|
536
594
|
}
|
|
537
595
|
]
|
|
@@ -541,7 +599,7 @@
|
|
|
541
599
|
"title": "${config.title.timer}",
|
|
542
600
|
"description": "${config.description.timer}",
|
|
543
601
|
"condition": {
|
|
544
|
-
"functionBody": "const type = model.accessories?.[arguments[1]]?.type; return type
|
|
602
|
+
"functionBody": "const type = model.accessories?.[arguments[1]]?.type; return type && type !== 'Thermostat';"
|
|
545
603
|
},
|
|
546
604
|
"items": [
|
|
547
605
|
{
|
|
@@ -551,11 +609,14 @@
|
|
|
551
609
|
"items": [
|
|
552
610
|
{
|
|
553
611
|
"key": "accessories[].timer.delay",
|
|
554
|
-
"flex": "
|
|
612
|
+
"flex": "0 0 33%"
|
|
555
613
|
},
|
|
556
614
|
{
|
|
557
615
|
"key": "accessories[].timer.units",
|
|
558
|
-
"flex": "
|
|
616
|
+
"flex": "0 0 33%",
|
|
617
|
+
"condition": {
|
|
618
|
+
"functionBody": "return model.accessories?.[arguments[1]]?.timer?.delay;"
|
|
619
|
+
}
|
|
559
620
|
}
|
|
560
621
|
]
|
|
561
622
|
},
|
|
@@ -563,14 +624,21 @@
|
|
|
563
624
|
"type": "fieldset",
|
|
564
625
|
"notitle": true,
|
|
565
626
|
"condition": {
|
|
566
|
-
"functionBody": "return model.accessories?.[arguments[1]]?.timer?.delay
|
|
627
|
+
"functionBody": "return model.accessories?.[arguments[1]]?.timer?.delay;"
|
|
567
628
|
},
|
|
568
629
|
"items": [
|
|
569
630
|
"accessories[].timer.random",
|
|
631
|
+
{
|
|
632
|
+
"key": "accessories[].fadeOut",
|
|
633
|
+
"flex": "1 0 33%",
|
|
634
|
+
"condition": {
|
|
635
|
+
"functionBody": "return model.accessories?.[arguments[1]]?.defaultBrightness !== undefined && model.accessories?.[arguments[1]]?.defaultState !== 'on' && model.accessories?.[arguments[1]]?.timer?.delay !== undefined;"
|
|
636
|
+
}
|
|
637
|
+
},
|
|
570
638
|
{
|
|
571
639
|
"key": "accessories[].sensor.timerControlled",
|
|
572
640
|
"condition": {
|
|
573
|
-
"functionBody": "return model.accessories?.[arguments[1]]?.sensor?.type
|
|
641
|
+
"functionBody": "return model.accessories?.[arguments[1]]?.sensor?.type;"
|
|
574
642
|
}
|
|
575
643
|
}
|
|
576
644
|
]
|
|
@@ -582,7 +650,7 @@
|
|
|
582
650
|
"title": "${config.title.limiter}",
|
|
583
651
|
"description": "${config.description.limiter}",
|
|
584
652
|
"condition": {
|
|
585
|
-
"functionBody": "const type = model.accessories?.[arguments[1]]?.type; return type
|
|
653
|
+
"functionBody": "const type = model.accessories?.[arguments[1]]?.type; return type && type !== 'Thermostat';"
|
|
586
654
|
},
|
|
587
655
|
"items": [
|
|
588
656
|
{
|
|
@@ -592,15 +660,71 @@
|
|
|
592
660
|
"items": [
|
|
593
661
|
{
|
|
594
662
|
"key": "accessories[].limiter.limit",
|
|
595
|
-
"flex": "
|
|
663
|
+
"flex": "0 0 33%"
|
|
596
664
|
},
|
|
597
665
|
{
|
|
598
666
|
"key": "accessories[].limiter.units",
|
|
599
|
-
"flex": "
|
|
667
|
+
"flex": "0 0 33%",
|
|
668
|
+
"condition": {
|
|
669
|
+
"functionBody": "return model.accessories?.[arguments[1]]?.limiter?.limit;"
|
|
670
|
+
}
|
|
600
671
|
},
|
|
601
672
|
{
|
|
602
673
|
"key": "accessories[].limiter.period",
|
|
603
|
-
"flex": "
|
|
674
|
+
"flex": "0 0 33%",
|
|
675
|
+
"condition": {
|
|
676
|
+
"functionBody": "return model.accessories?.[arguments[1]]?.limiter?.limit;"
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
]
|
|
680
|
+
}
|
|
681
|
+
]
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
"type": "fieldset",
|
|
685
|
+
"title": "${config.title.conditions}",
|
|
686
|
+
"description": "${config.description.conditions}",
|
|
687
|
+
"condition": {
|
|
688
|
+
"functionBody": "const type = model.accessories?.[arguments[1]]?.type; return model.accessories?.length > 1 && type && type !== 'Thermostat'"
|
|
689
|
+
},
|
|
690
|
+
"items": [
|
|
691
|
+
{
|
|
692
|
+
"type": "div",
|
|
693
|
+
"displayFlex": true,
|
|
694
|
+
"flex-direction": "row",
|
|
695
|
+
"items": [
|
|
696
|
+
{
|
|
697
|
+
"key": "accessories[].conditions.operator",
|
|
698
|
+
"flex": "0 0 67%"
|
|
699
|
+
}
|
|
700
|
+
]
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
"type": "array",
|
|
704
|
+
"title": "${config.title.condition}",
|
|
705
|
+
"notitle": true,
|
|
706
|
+
"condition": {
|
|
707
|
+
"functionBody": "return model.accessories?.[arguments[1]]?.conditions?.operator;"
|
|
708
|
+
},
|
|
709
|
+
"key": "accessories[].conditions.operands",
|
|
710
|
+
"items": [
|
|
711
|
+
{
|
|
712
|
+
"type": "div",
|
|
713
|
+
"displayFlex": true,
|
|
714
|
+
"flex-direction": "row",
|
|
715
|
+
"items": [
|
|
716
|
+
{
|
|
717
|
+
"key": "accessories[].conditions.operands[].accessoryId",
|
|
718
|
+
"flex": "0 0 50%"
|
|
719
|
+
},
|
|
720
|
+
{
|
|
721
|
+
"key": "accessories[].conditions.operands[].accessoryState",
|
|
722
|
+
"flex": "0 0 50%",
|
|
723
|
+
"condition": {
|
|
724
|
+
"functionBody": "return model.accessories?.[arguments[1][0]]?.conditions?.operands?.[arguments[1][1]]?.accessoryId;"
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
]
|
|
604
728
|
}
|
|
605
729
|
]
|
|
606
730
|
}
|
|
@@ -611,7 +735,7 @@
|
|
|
611
735
|
"title": "${config.title.commands}",
|
|
612
736
|
"description": "${config.description.commands}",
|
|
613
737
|
"condition": {
|
|
614
|
-
"functionBody": "return model.accessories?.[arguments[1]]?.type
|
|
738
|
+
"functionBody": "return model.accessories?.[arguments[1]]?.type;"
|
|
615
739
|
},
|
|
616
740
|
"items": [
|
|
617
741
|
{
|
package/dist/accessory/base.d.ts
CHANGED
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
import { PlatformAccessory, Service } from 'homebridge';
|
|
2
2
|
import { SensorAccessory } from './sensor.js';
|
|
3
|
-
import {
|
|
3
|
+
import { ConditionManager } from '../model/conditions.js';
|
|
4
|
+
import { AccessoryState, AccessoryType } from '../model/enums.js';
|
|
4
5
|
import { CharacteristicType, DummyConfig, ServiceType } from '../model/types.js';
|
|
5
6
|
import { Webhook } from '../model/webhook.js';
|
|
6
7
|
import { Log } from '../tools/log.js';
|
|
8
|
+
export type DummyAccessoryDependency<C extends DummyConfig> = {
|
|
9
|
+
Service: ServiceType;
|
|
10
|
+
Characteristic: CharacteristicType;
|
|
11
|
+
platformAccessory: PlatformAccessory;
|
|
12
|
+
config: C;
|
|
13
|
+
conditionManager: ConditionManager;
|
|
14
|
+
log: Log;
|
|
15
|
+
isGrouped: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type DummyAddonDependency = {
|
|
18
|
+
Service: ServiceType;
|
|
19
|
+
Characteristic: CharacteristicType;
|
|
20
|
+
platformAccessory: PlatformAccessory;
|
|
21
|
+
identifier: string;
|
|
22
|
+
caller: string;
|
|
23
|
+
log: Log;
|
|
24
|
+
disableLogging: boolean;
|
|
25
|
+
};
|
|
7
26
|
export declare abstract class DummyAccessory<C extends DummyConfig> {
|
|
8
|
-
|
|
9
|
-
protected readonly Characteristic: CharacteristicType;
|
|
10
|
-
protected readonly accessory: PlatformAccessory;
|
|
11
|
-
protected readonly config: C;
|
|
12
|
-
protected readonly log: Log;
|
|
27
|
+
private readonly dependency;
|
|
13
28
|
protected sensor?: SensorAccessory;
|
|
14
29
|
static identifier(config: DummyConfig): string;
|
|
15
30
|
protected readonly accessoryService: Service;
|
|
@@ -17,20 +32,25 @@ export declare abstract class DummyAccessory<C extends DummyConfig> {
|
|
|
17
32
|
private readonly _timer?;
|
|
18
33
|
private readonly _limiter?;
|
|
19
34
|
private readonly execAsync;
|
|
20
|
-
constructor(
|
|
35
|
+
constructor(dependency: DummyAccessoryDependency<C>);
|
|
21
36
|
protected abstract getAccessoryType(): AccessoryType;
|
|
22
|
-
protected abstract
|
|
37
|
+
protected abstract trigger(): Promise<void>;
|
|
23
38
|
protected abstract reset(): Promise<void>;
|
|
24
39
|
get subtype(): string | undefined;
|
|
25
40
|
teardown(): void;
|
|
26
41
|
abstract webhooks(): Webhook[];
|
|
42
|
+
protected get config(): C;
|
|
27
43
|
protected get identifier(): string;
|
|
28
44
|
protected get name(): string;
|
|
45
|
+
protected get log(): Log;
|
|
46
|
+
protected get Characteristic(): CharacteristicType;
|
|
29
47
|
protected get isStateful(): boolean;
|
|
30
48
|
protected get defaultStateStorageKey(): string;
|
|
31
49
|
protected startTimer(): void;
|
|
50
|
+
protected onTimerStarted(_delay: number): void;
|
|
32
51
|
protected cancelTimer(): void;
|
|
33
52
|
protected executeCommand(command: string): Promise<void>;
|
|
34
53
|
private isExecException;
|
|
54
|
+
protected onStateChange(state: AccessoryState): Promise<void>;
|
|
35
55
|
protected logIfDesired(message: string, ...parameters: (string | number)[]): void;
|
|
36
56
|
}
|
package/dist/accessory/base.js
CHANGED
|
@@ -9,11 +9,7 @@ import { Schedule } from '../timeout/schedule.js';
|
|
|
9
9
|
import { Timer } from '../timeout/timer.js';
|
|
10
10
|
import getVersion from '../tools/version.js';
|
|
11
11
|
export class DummyAccessory {
|
|
12
|
-
|
|
13
|
-
Characteristic;
|
|
14
|
-
accessory;
|
|
15
|
-
config;
|
|
16
|
-
log;
|
|
12
|
+
dependency;
|
|
17
13
|
sensor;
|
|
18
14
|
static identifier(config) {
|
|
19
15
|
return config.id ?? `${PLATFORM_NAME}:${config.type}:${config.name.replace(/\s+/g, '')}`;
|
|
@@ -23,44 +19,47 @@ export class DummyAccessory {
|
|
|
23
19
|
_timer;
|
|
24
20
|
_limiter;
|
|
25
21
|
execAsync = promisify(exec);
|
|
26
|
-
constructor(
|
|
27
|
-
this.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
22
|
+
constructor(dependency) {
|
|
23
|
+
this.dependency = dependency;
|
|
24
|
+
const name = dependency.config.name;
|
|
25
|
+
const disableLogging = dependency.config.disableLogging === true;
|
|
26
|
+
const addonDependency = {
|
|
27
|
+
Service: dependency.Service,
|
|
28
|
+
Characteristic: dependency.Characteristic,
|
|
29
|
+
platformAccessory: dependency.platformAccessory,
|
|
30
|
+
identifier: this.identifier,
|
|
31
|
+
caller: name,
|
|
32
|
+
log: dependency.log,
|
|
33
|
+
disableLogging: disableLogging,
|
|
34
|
+
};
|
|
35
|
+
this.sensor = SensorAccessory.new(addonDependency, dependency.config.sensor);
|
|
36
|
+
this._timer = Timer.new(addonDependency, dependency.config.timer);
|
|
37
|
+
this._schedule = Schedule.new(addonDependency, dependency.config.schedule, this.trigger.bind(this));
|
|
38
|
+
this._limiter = Limiter.new(addonDependency, dependency.config.limiter);
|
|
39
|
+
dependency.conditionManager.register(name, this.identifier, dependency.config.conditions, this.trigger.bind(this), this._timer ? undefined : this.reset.bind(this), disableLogging);
|
|
40
|
+
const serviceInstance = dependency.Service[this.getAccessoryType()];
|
|
41
|
+
if (dependency.isGrouped) {
|
|
42
|
+
let accessoryService = dependency.platformAccessory.getServiceById(serviceInstance, this.identifier);
|
|
45
43
|
if (!accessoryService) {
|
|
46
|
-
accessoryService =
|
|
47
|
-
accessoryService.
|
|
44
|
+
accessoryService = dependency.platformAccessory.addService(serviceInstance, name, this.identifier);
|
|
45
|
+
accessoryService.addOptionalCharacteristic(dependency.Characteristic.ConfiguredName);
|
|
46
|
+
accessoryService.setCharacteristic(dependency.Characteristic.ConfiguredName, name);
|
|
48
47
|
}
|
|
49
48
|
this.accessoryService = accessoryService;
|
|
50
49
|
return;
|
|
51
50
|
}
|
|
52
|
-
|
|
53
|
-
.setCharacteristic(Characteristic.Name,
|
|
54
|
-
.setCharacteristic(Characteristic.ConfiguredName,
|
|
55
|
-
.setCharacteristic(Characteristic.Manufacturer, PLUGIN_ALIAS)
|
|
56
|
-
.setCharacteristic(Characteristic.Model, config.type)
|
|
57
|
-
.setCharacteristic(Characteristic.SerialNumber, this.identifier)
|
|
58
|
-
.setCharacteristic(Characteristic.FirmwareRevision, getVersion());
|
|
59
|
-
this.accessoryService =
|
|
51
|
+
dependency.platformAccessory.getService(dependency.Service.AccessoryInformation)
|
|
52
|
+
.setCharacteristic(dependency.Characteristic.Name, name)
|
|
53
|
+
.setCharacteristic(dependency.Characteristic.ConfiguredName, name)
|
|
54
|
+
.setCharacteristic(dependency.Characteristic.Manufacturer, PLUGIN_ALIAS)
|
|
55
|
+
.setCharacteristic(dependency.Characteristic.Model, dependency.config.type)
|
|
56
|
+
.setCharacteristic(dependency.Characteristic.SerialNumber, this.identifier)
|
|
57
|
+
.setCharacteristic(dependency.Characteristic.FirmwareRevision, getVersion());
|
|
58
|
+
this.accessoryService = dependency.platformAccessory.getService(serviceInstance) || dependency.platformAccessory.addService(serviceInstance);
|
|
60
59
|
for (const type of Object.values(AccessoryType)) {
|
|
61
|
-
const existingService =
|
|
60
|
+
const existingService = dependency.platformAccessory.getService(dependency.Service[type]);
|
|
62
61
|
if (existingService && type !== this.getAccessoryType()) {
|
|
63
|
-
|
|
62
|
+
dependency.platformAccessory.removeService(existingService);
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
}
|
|
@@ -72,12 +71,21 @@ export class DummyAccessory {
|
|
|
72
71
|
this._schedule?.teardown();
|
|
73
72
|
this._limiter?.teardown();
|
|
74
73
|
}
|
|
74
|
+
get config() {
|
|
75
|
+
return this.dependency.config;
|
|
76
|
+
}
|
|
75
77
|
get identifier() {
|
|
76
78
|
return DummyAccessory.identifier(this.config);
|
|
77
79
|
}
|
|
78
80
|
get name() {
|
|
79
81
|
return this.config.name;
|
|
80
82
|
}
|
|
83
|
+
get log() {
|
|
84
|
+
return this.dependency.log;
|
|
85
|
+
}
|
|
86
|
+
get Characteristic() {
|
|
87
|
+
return this.dependency.Characteristic;
|
|
88
|
+
}
|
|
81
89
|
get isStateful() {
|
|
82
90
|
return this._schedule === undefined && !this.config.resetOnRestart;
|
|
83
91
|
}
|
|
@@ -85,9 +93,14 @@ export class DummyAccessory {
|
|
|
85
93
|
return `${this.identifier}:DefaultState`;
|
|
86
94
|
}
|
|
87
95
|
startTimer() {
|
|
88
|
-
this._timer
|
|
96
|
+
if (this._timer) {
|
|
97
|
+
const delay = this._timer.start(this.reset.bind(this));
|
|
98
|
+
this.onTimerStarted(delay);
|
|
99
|
+
}
|
|
89
100
|
this._limiter?.start(this.reset.bind(this));
|
|
90
101
|
}
|
|
102
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
103
|
+
onTimerStarted(_delay) { }
|
|
91
104
|
cancelTimer() {
|
|
92
105
|
this._timer?.cancel();
|
|
93
106
|
this._limiter?.cancel();
|
|
@@ -128,6 +141,9 @@ export class DummyAccessory {
|
|
|
128
141
|
'stderr' in err &&
|
|
129
142
|
typeof err.stderr === 'string';
|
|
130
143
|
}
|
|
144
|
+
async onStateChange(state) {
|
|
145
|
+
await this.dependency.conditionManager.onStateChange(this.identifier, state);
|
|
146
|
+
}
|
|
131
147
|
logIfDesired(message, ...parameters) {
|
|
132
148
|
if (this.config.disableLogging) {
|
|
133
149
|
return;
|