homebridge-securitysystem 10.0.0-beta.2 → 10.0.0-beta.4
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/dist/conditions/already-triggered-condition.d.ts +1 -1
- package/dist/conditions/already-triggered-condition.js +6 -2
- package/dist/conditions/already-triggered-condition.js.map +1 -1
- package/dist/conditions/arming-in-progress-condition.d.ts +1 -1
- package/dist/conditions/arming-in-progress-condition.js +6 -2
- package/dist/conditions/arming-in-progress-condition.js.map +1 -1
- package/dist/conditions/double-knock-condition.d.ts +4 -2
- package/dist/conditions/double-knock-condition.js +9 -8
- package/dist/conditions/double-knock-condition.js.map +1 -1
- package/dist/conditions/not-armed-condition.d.ts +1 -1
- package/dist/conditions/not-armed-condition.js +6 -2
- package/dist/conditions/not-armed-condition.js.map +1 -1
- package/dist/conditions/trigger-already-running-condition.d.ts +10 -0
- package/dist/conditions/trigger-already-running-condition.js +19 -0
- package/dist/conditions/trigger-already-running-condition.js.map +1 -0
- package/dist/handlers/state-handler.d.ts +14 -9
- package/dist/handlers/state-handler.js +44 -75
- package/dist/handlers/state-handler.js.map +1 -1
- package/dist/handlers/switch-handler.d.ts +12 -5
- package/dist/handlers/switch-handler.js +17 -22
- package/dist/handlers/switch-handler.js.map +1 -1
- package/dist/handlers/trip-handler.d.ts +9 -11
- package/dist/handlers/trip-handler.js +34 -55
- package/dist/handlers/trip-handler.js.map +1 -1
- package/dist/homekit/homekit-registrar.d.ts +19 -0
- package/dist/homekit/homekit-registrar.js +134 -0
- package/dist/homekit/homekit-registrar.js.map +1 -0
- package/dist/homekit/service-factory.d.ts +9 -0
- package/dist/homekit/service-factory.js +117 -0
- package/dist/homekit/service-factory.js.map +1 -0
- package/dist/interfaces/condition-context-interface.d.ts +3 -0
- package/dist/interfaces/system-state-interface.d.ts +1 -7
- package/dist/security-system.d.ts +1 -5
- package/dist/security-system.js +39 -269
- package/dist/security-system.js.map +1 -1
- package/dist/services/configuration-service.d.ts +24 -0
- package/dist/services/configuration-service.js +187 -0
- package/dist/services/configuration-service.js.map +1 -0
- package/dist/services/server-service.js +1 -1
- package/dist/services/server-service.js.map +1 -1
- package/dist/tests/conditions.test.js +10 -13
- package/dist/tests/conditions.test.js.map +1 -1
- package/dist/tests/state-handler.test.js +21 -24
- package/dist/tests/state-handler.test.js.map +1 -1
- package/dist/tests/trip-handler.test.js +33 -24
- package/dist/tests/trip-handler.test.js.map +1 -1
- package/dist/timers/timer-manager.d.ts +34 -0
- package/dist/timers/timer-manager.js +132 -0
- package/dist/timers/timer-manager.js.map +1 -0
- package/dist/types/event-payload-map-type.d.ts +6 -1
- package/dist/types/event-type.d.ts +15 -1
- package/dist/types/event-type.js +8 -0
- package/dist/types/event-type.js.map +1 -1
- package/dist/utils/arming-util.d.ts +19 -0
- package/dist/utils/arming-util.js +24 -0
- package/dist/utils/arming-util.js.map +1 -0
- package/package.json +12 -12
- package/src/conditions/already-triggered-condition.ts +7 -3
- package/src/conditions/arming-in-progress-condition.ts +7 -3
- package/src/conditions/double-knock-condition.ts +11 -11
- package/src/conditions/not-armed-condition.ts +7 -3
- package/src/conditions/trigger-already-running-condition.ts +21 -0
- package/src/handlers/state-handler.ts +51 -85
- package/src/handlers/switch-handler.ts +21 -31
- package/src/handlers/trip-handler.ts +42 -66
- package/src/homekit/homekit-registrar.ts +147 -0
- package/src/homekit/service-factory.ts +139 -0
- package/src/interfaces/condition-context-interface.ts +3 -0
- package/src/interfaces/system-state-interface.ts +1 -9
- package/src/security-system.ts +44 -298
- package/src/services/configuration-service.ts +220 -0
- package/src/services/server-service.ts +1 -1
- package/src/tests/conditions.test.ts +11 -13
- package/src/tests/state-handler.test.ts +22 -24
- package/src/tests/trip-handler.test.ts +41 -26
- package/src/timers/timer-manager.ts +158 -0
- package/src/types/event-payload-map-type.ts +8 -0
- package/src/types/event-type.ts +22 -0
- package/src/utils/arming-util.ts +44 -0
- package/dist/utils/options-util.d.ts +0 -6
- package/dist/utils/options-util.js +0 -171
- package/dist/utils/options-util.js.map +0 -1
- package/src/utils/options-util.ts +0 -202
|
@@ -6,5 +6,5 @@ import { Condition } from './condition.js';
|
|
|
6
6
|
*/
|
|
7
7
|
export declare class AlreadyTriggeredCondition extends Condition {
|
|
8
8
|
readonly name = "already-triggered";
|
|
9
|
-
evaluate({ state, value }: ConditionContext): boolean;
|
|
9
|
+
evaluate({ state, value, log }: ConditionContext): boolean;
|
|
10
10
|
}
|
|
@@ -6,11 +6,15 @@ import { Condition } from './condition.js';
|
|
|
6
6
|
*/
|
|
7
7
|
export class AlreadyTriggeredCondition extends Condition {
|
|
8
8
|
name = 'already-triggered';
|
|
9
|
-
evaluate({ state, value }) {
|
|
9
|
+
evaluate({ state, value, log }) {
|
|
10
10
|
if (!value) {
|
|
11
11
|
return false;
|
|
12
12
|
}
|
|
13
|
-
|
|
13
|
+
const blocked = state.currentState === SecurityState.TRIGGERED;
|
|
14
|
+
if (blocked) {
|
|
15
|
+
log.warn('Security System (Already triggered): alarm is already active');
|
|
16
|
+
}
|
|
17
|
+
return blocked;
|
|
14
18
|
}
|
|
15
19
|
}
|
|
16
20
|
//# sourceMappingURL=already-triggered-condition.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"already-triggered-condition.js","sourceRoot":"","sources":["../../src/conditions/already-triggered-condition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAEhE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C;;;GAGG;AACH,MAAM,OAAO,yBAA0B,SAAQ,SAAS;IAC7C,IAAI,GAAG,mBAAmB,CAAC;IAEpC,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAoB;
|
|
1
|
+
{"version":3,"file":"already-triggered-condition.js","sourceRoot":"","sources":["../../src/conditions/already-triggered-condition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAEhE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C;;;GAGG;AACH,MAAM,OAAO,yBAA0B,SAAQ,SAAS;IAC7C,IAAI,GAAG,mBAAmB,CAAC;IAEpC,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAoB;QAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,YAAY,KAAK,aAAa,CAAC,SAAS,CAAC;QAC/D,IAAI,OAAO,EAAE,CAAC;YACZ,GAAG,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;QAC3E,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
|
|
@@ -6,5 +6,5 @@ import { Condition } from './condition.js';
|
|
|
6
6
|
*/
|
|
7
7
|
export declare class ArmingInProgressCondition extends Condition {
|
|
8
8
|
readonly name = "arming-in-progress";
|
|
9
|
-
evaluate({ state, value }: ConditionContext): boolean;
|
|
9
|
+
evaluate({ state, value, log }: ConditionContext): boolean;
|
|
10
10
|
}
|
|
@@ -5,11 +5,15 @@ import { Condition } from './condition.js';
|
|
|
5
5
|
*/
|
|
6
6
|
export class ArmingInProgressCondition extends Condition {
|
|
7
7
|
name = 'arming-in-progress';
|
|
8
|
-
evaluate({ state, value }) {
|
|
8
|
+
evaluate({ state, value, log }) {
|
|
9
9
|
if (!value) {
|
|
10
10
|
return false;
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
if (state.isArming) {
|
|
13
|
+
log.warn('Trip Switch (Still arming): arm delay countdown is still in progress');
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
13
17
|
}
|
|
14
18
|
}
|
|
15
19
|
//# sourceMappingURL=arming-in-progress-condition.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arming-in-progress-condition.js","sourceRoot":"","sources":["../../src/conditions/arming-in-progress-condition.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C;;;GAGG;AACH,MAAM,OAAO,yBAA0B,SAAQ,SAAS;IAC7C,IAAI,GAAG,oBAAoB,CAAC;IAErC,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAoB;
|
|
1
|
+
{"version":3,"file":"arming-in-progress-condition.js","sourceRoot":"","sources":["../../src/conditions/arming-in-progress-condition.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C;;;GAGG;AACH,MAAM,OAAO,yBAA0B,SAAQ,SAAS;IAC7C,IAAI,GAAG,oBAAoB,CAAC;IAErC,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAoB;QAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACnB,GAAG,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;YACjF,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
|
|
@@ -10,13 +10,15 @@ import { Condition } from './condition.js';
|
|
|
10
10
|
*/
|
|
11
11
|
export declare class DoubleKnockCondition extends Condition {
|
|
12
12
|
private readonly onFirstKnock;
|
|
13
|
+
private readonly onCancelTimer;
|
|
13
14
|
readonly name = "double-knock";
|
|
14
15
|
/**
|
|
15
16
|
* @param onFirstKnock Called when the first knock is detected.
|
|
16
17
|
* The callback receives the window duration in seconds and a reset function
|
|
17
18
|
* to invoke when the window expires.
|
|
19
|
+
* @param onCancelTimer Called on the second knock to cancel the running window timer.
|
|
18
20
|
*/
|
|
19
|
-
constructor(onFirstKnock: (seconds: number, onExpire: () => void) => void);
|
|
20
|
-
evaluate({ state, options, value, origin }: ConditionContext): boolean;
|
|
21
|
+
constructor(onFirstKnock: (seconds: number, onExpire: () => void) => void, onCancelTimer: () => void);
|
|
22
|
+
evaluate({ state, options, value, origin, log }: ConditionContext): boolean;
|
|
21
23
|
private resolveWindow;
|
|
22
24
|
}
|
|
@@ -12,17 +12,20 @@ import { Condition } from './condition.js';
|
|
|
12
12
|
*/
|
|
13
13
|
export class DoubleKnockCondition extends Condition {
|
|
14
14
|
onFirstKnock;
|
|
15
|
+
onCancelTimer;
|
|
15
16
|
name = 'double-knock';
|
|
16
17
|
/**
|
|
17
18
|
* @param onFirstKnock Called when the first knock is detected.
|
|
18
19
|
* The callback receives the window duration in seconds and a reset function
|
|
19
20
|
* to invoke when the window expires.
|
|
21
|
+
* @param onCancelTimer Called on the second knock to cancel the running window timer.
|
|
20
22
|
*/
|
|
21
|
-
constructor(onFirstKnock) {
|
|
23
|
+
constructor(onFirstKnock, onCancelTimer) {
|
|
22
24
|
super();
|
|
23
25
|
this.onFirstKnock = onFirstKnock;
|
|
26
|
+
this.onCancelTimer = onCancelTimer;
|
|
24
27
|
}
|
|
25
|
-
evaluate({ state, options, value, origin }) {
|
|
28
|
+
evaluate({ state, options, value, origin, log }) {
|
|
26
29
|
if (!value || !options.doubleKnock) {
|
|
27
30
|
return false;
|
|
28
31
|
}
|
|
@@ -37,12 +40,9 @@ export class DoubleKnockCondition extends Condition {
|
|
|
37
40
|
return false;
|
|
38
41
|
}
|
|
39
42
|
if (state.isKnocked) {
|
|
40
|
-
// Second knock — clear the flag and allow through.
|
|
43
|
+
// Second knock — clear the flag and cancel the window timer, then allow through.
|
|
41
44
|
state.isKnocked = false;
|
|
42
|
-
|
|
43
|
-
clearTimeout(state.doubleKnockTimeout);
|
|
44
|
-
state.doubleKnockTimeout = null;
|
|
45
|
-
}
|
|
45
|
+
this.onCancelTimer();
|
|
46
46
|
return false;
|
|
47
47
|
}
|
|
48
48
|
// First knock — block and schedule expiry.
|
|
@@ -50,8 +50,9 @@ export class DoubleKnockCondition extends Condition {
|
|
|
50
50
|
const seconds = this.resolveWindow(state.currentState, options);
|
|
51
51
|
this.onFirstKnock(seconds, () => {
|
|
52
52
|
state.isKnocked = false;
|
|
53
|
-
|
|
53
|
+
log.info('Trip Switch (Reset): double-knock window expired without second activation');
|
|
54
54
|
});
|
|
55
|
+
log.warn('Trip Switch (Knock): double-knock is required, waiting for second activation');
|
|
55
56
|
return true;
|
|
56
57
|
}
|
|
57
58
|
resolveWindow(currentState, options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"double-knock-condition.js","sourceRoot":"","sources":["../../src/conditions/double-knock-condition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,OAAO,oBAAqB,SAAQ,SAAS;
|
|
1
|
+
{"version":3,"file":"double-knock-condition.js","sourceRoot":"","sources":["../../src/conditions/double-knock-condition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,OAAO,oBAAqB,SAAQ,SAAS;IAU9B;IACA;IAVV,IAAI,GAAG,cAAc,CAAC;IAE/B;;;;;OAKG;IACH,YACmB,YAA6D,EAC7D,aAAyB;QAE1C,KAAK,EAAE,CAAC;QAHS,iBAAY,GAAZ,YAAY,CAAiD;QAC7D,kBAAa,GAAb,aAAa,CAAY;IAG5C,CAAC;IAED,QAAQ,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAoB;QAC/D,IAAI,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YACnC,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,MAAM,KAAK,UAAU,CAAC,eAAe,EAAE,CAAC;YAC1C,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACnB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QACxF,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC;YAClD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACpB,iFAAiF;YACjF,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;YACxB,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,2CAA2C;QAC3C,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAEhE,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,GAAG,EAAE;YAC9B,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC;YACxB,GAAG,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAC;QACzF,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,IAAI,CAAC,8EAA8E,CAAC,CAAC;QACzF,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,aAAa,CACnB,YAA2B,EAC3B,OAAoC;QAEpC,IAAI,YAAY,KAAK,aAAa,CAAC,IAAI,IAAI,OAAO,CAAC,sBAAsB,KAAK,IAAI,EAAE,CAAC;YACnF,OAAO,OAAO,CAAC,sBAAsB,CAAC;QACxC,CAAC;QACD,IAAI,YAAY,KAAK,aAAa,CAAC,IAAI,IAAI,OAAO,CAAC,sBAAsB,KAAK,IAAI,EAAE,CAAC;YACnF,OAAO,OAAO,CAAC,sBAAsB,CAAC;QACxC,CAAC;QACD,IAAI,YAAY,KAAK,aAAa,CAAC,KAAK,IAAI,OAAO,CAAC,uBAAuB,KAAK,IAAI,EAAE,CAAC;YACrF,OAAO,OAAO,CAAC,uBAAuB,CAAC;QACzC,CAAC;QACD,OAAO,OAAO,CAAC,kBAAkB,CAAC;IACpC,CAAC;CACF"}
|
|
@@ -6,5 +6,5 @@ import { Condition } from './condition.js';
|
|
|
6
6
|
*/
|
|
7
7
|
export declare class NotArmedCondition extends Condition {
|
|
8
8
|
readonly name = "not-armed";
|
|
9
|
-
evaluate({ state, options, value, origin }: ConditionContext): boolean;
|
|
9
|
+
evaluate({ state, options, value, origin, log }: ConditionContext): boolean;
|
|
10
10
|
}
|
|
@@ -7,14 +7,18 @@ import { Condition } from './condition.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export class NotArmedCondition extends Condition {
|
|
9
9
|
name = 'not-armed';
|
|
10
|
-
evaluate({ state, options, value, origin }) {
|
|
10
|
+
evaluate({ state, options, value, origin, log }) {
|
|
11
11
|
if (!value) {
|
|
12
12
|
return false;
|
|
13
13
|
}
|
|
14
14
|
const isDisarmed = state.currentState === SecurityState.OFF;
|
|
15
15
|
const isNotOverridingOff = !options.overrideOff;
|
|
16
16
|
const isNotOverrideSwitch = origin !== OriginType.OVERRIDE_SWITCH;
|
|
17
|
-
|
|
17
|
+
const blocked = isDisarmed && isNotOverridingOff && isNotOverrideSwitch;
|
|
18
|
+
if (blocked) {
|
|
19
|
+
log.warn('Trip Switch (Not armed): system is disarmed and override is not enabled');
|
|
20
|
+
}
|
|
21
|
+
return blocked;
|
|
18
22
|
}
|
|
19
23
|
}
|
|
20
24
|
//# sourceMappingURL=not-armed-condition.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"not-armed-condition.js","sourceRoot":"","sources":["../../src/conditions/not-armed-condition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C;;;GAGG;AACH,MAAM,OAAO,iBAAkB,SAAQ,SAAS;IACrC,IAAI,GAAG,WAAW,CAAC;IAE5B,QAAQ,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAoB;
|
|
1
|
+
{"version":3,"file":"not-armed-condition.js","sourceRoot":"","sources":["../../src/conditions/not-armed-condition.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAErD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C;;;GAGG;AACH,MAAM,OAAO,iBAAkB,SAAQ,SAAS;IACrC,IAAI,GAAG,WAAW,CAAC;IAE5B,QAAQ,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAoB;QAC/D,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,UAAU,GAAG,KAAK,CAAC,YAAY,KAAK,aAAa,CAAC,GAAG,CAAC;QAC5D,MAAM,kBAAkB,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC;QAChD,MAAM,mBAAmB,GAAG,MAAM,KAAK,UAAU,CAAC,eAAe,CAAC;QAElE,MAAM,OAAO,GAAG,UAAU,IAAI,kBAAkB,IAAI,mBAAmB,CAAC;QACxE,IAAI,OAAO,EAAE,CAAC;YACZ,GAAG,CAAC,IAAI,CAAC,yEAAyE,CAAC,CAAC;QACtF,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ConditionContext } from '../interfaces/condition-context-interface.js';
|
|
2
|
+
import { Condition } from './condition.js';
|
|
3
|
+
/**
|
|
4
|
+
* Blocks a trip activation when the trigger-delay countdown is already running.
|
|
5
|
+
* Prevents stacking multiple trigger events during the pre-alarm window.
|
|
6
|
+
*/
|
|
7
|
+
export declare class TriggerAlreadyRunningCondition extends Condition {
|
|
8
|
+
readonly name = "trigger-already-running";
|
|
9
|
+
evaluate({ state, value, log }: ConditionContext): boolean;
|
|
10
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Condition } from './condition.js';
|
|
2
|
+
/**
|
|
3
|
+
* Blocks a trip activation when the trigger-delay countdown is already running.
|
|
4
|
+
* Prevents stacking multiple trigger events during the pre-alarm window.
|
|
5
|
+
*/
|
|
6
|
+
export class TriggerAlreadyRunningCondition extends Condition {
|
|
7
|
+
name = 'trigger-already-running';
|
|
8
|
+
evaluate({ state, value, log }) {
|
|
9
|
+
if (!value) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if (state.isTripping) {
|
|
13
|
+
log.warn('Security System (Already tripped): trigger delay countdown is already running');
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=trigger-already-running-condition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trigger-already-running-condition.js","sourceRoot":"","sources":["../../src/conditions/trigger-already-running-condition.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C;;;GAGG;AACH,MAAM,OAAO,8BAA+B,SAAQ,SAAS;IAClD,IAAI,GAAG,yBAAyB,CAAC;IAE1C,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAoB;QAC9C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACrB,GAAG,CAAC,IAAI,CAAC,+EAA+E,CAAC,CAAC;YAC1F,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
|
|
@@ -8,10 +8,13 @@ import type { SecuritySystemOptions } from '../interfaces/options-interface.js';
|
|
|
8
8
|
import type { EventBusService } from '../services/event-bus-service.js';
|
|
9
9
|
import type { StorageService } from '../services/storage-service.js';
|
|
10
10
|
import type { AudioService } from '../services/audio-service.js';
|
|
11
|
-
import type { TripHandler } from './trip-handler.js';
|
|
12
|
-
import type { SwitchHandler } from './switch-handler.js';
|
|
13
11
|
import type { SensorHandler } from './sensor-handler.js';
|
|
14
|
-
|
|
12
|
+
import type { TimerManager } from '../timers/timer-manager.js';
|
|
13
|
+
/**
|
|
14
|
+
* Manages the core security-system state machine: arming, triggering, and resetting.
|
|
15
|
+
* Cross-handler side effects are signalled via the event bus so that this class has
|
|
16
|
+
* no direct dependencies on TripHandler or SwitchHandler.
|
|
17
|
+
*/
|
|
15
18
|
export declare class StateHandler {
|
|
16
19
|
private readonly services;
|
|
17
20
|
private readonly state;
|
|
@@ -21,15 +24,17 @@ export declare class StateHandler {
|
|
|
21
24
|
private readonly bus;
|
|
22
25
|
private readonly storageService;
|
|
23
26
|
private readonly audio;
|
|
24
|
-
private
|
|
25
|
-
private
|
|
26
|
-
|
|
27
|
-
constructor(services: ServiceRegistry, state: SystemState, options: SecuritySystemOptions, Characteristic: CharacteristicConstructor, log: Logging, bus: EventBusService, storageService: StorageService, audio: AudioService);
|
|
28
|
-
setHandlers(trip: TripHandler, sw: SwitchHandler, sensor: SensorHandler): void;
|
|
27
|
+
private readonly timers;
|
|
28
|
+
private readonly sensorHandler;
|
|
29
|
+
constructor(services: ServiceRegistry, state: SystemState, options: SecuritySystemOptions, Characteristic: CharacteristicConstructor, log: Logging, bus: EventBusService, storageService: StorageService, audio: AudioService, timers: TimerManager, sensorHandler: SensorHandler);
|
|
29
30
|
setCurrentState(state: SecurityState, origin: OriginType): void;
|
|
30
31
|
updateTargetState(state: SecurityState, origin: OriginType, delay: number): boolean;
|
|
31
|
-
getArmingSeconds(
|
|
32
|
+
getArmingSeconds(targetState: SecurityState): number;
|
|
32
33
|
resetTimers(): void;
|
|
34
|
+
/** Returns true while the trigger delay is counting down (trip switch is active). */
|
|
35
|
+
isTripping(): boolean;
|
|
36
|
+
/** Checks whether arming is currently blocked by an arming-lock switch. */
|
|
37
|
+
isArmingLocked(targetState: SecurityState): boolean;
|
|
33
38
|
getAvailableTargetStates(): SecurityState[];
|
|
34
39
|
logMode(type: string, state: SecurityState | string): void;
|
|
35
40
|
private isBadTargetState;
|
|
@@ -3,7 +3,12 @@ import { OriginType } from '../types/origin-type.js';
|
|
|
3
3
|
import { stateToMode, capitalise } from '../utils/state-util.js';
|
|
4
4
|
import { modeToState } from '../utils/state-util.js';
|
|
5
5
|
import { EventType } from '../types/event-type.js';
|
|
6
|
-
|
|
6
|
+
import { getArmingSeconds } from '../utils/arming-util.js';
|
|
7
|
+
/**
|
|
8
|
+
* Manages the core security-system state machine: arming, triggering, and resetting.
|
|
9
|
+
* Cross-handler side effects are signalled via the event bus so that this class has
|
|
10
|
+
* no direct dependencies on TripHandler or SwitchHandler.
|
|
11
|
+
*/
|
|
7
12
|
export class StateHandler {
|
|
8
13
|
services;
|
|
9
14
|
state;
|
|
@@ -13,10 +18,9 @@ export class StateHandler {
|
|
|
13
18
|
bus;
|
|
14
19
|
storageService;
|
|
15
20
|
audio;
|
|
16
|
-
|
|
17
|
-
switchHandler;
|
|
21
|
+
timers;
|
|
18
22
|
sensorHandler;
|
|
19
|
-
constructor(services, state, options, Characteristic, log, bus, storageService, audio) {
|
|
23
|
+
constructor(services, state, options, Characteristic, log, bus, storageService, audio, timers, sensorHandler) {
|
|
20
24
|
this.services = services;
|
|
21
25
|
this.state = state;
|
|
22
26
|
this.options = options;
|
|
@@ -25,11 +29,8 @@ export class StateHandler {
|
|
|
25
29
|
this.bus = bus;
|
|
26
30
|
this.storageService = storageService;
|
|
27
31
|
this.audio = audio;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this.tripHandler = trip;
|
|
31
|
-
this.switchHandler = sw;
|
|
32
|
-
this.sensorHandler = sensor;
|
|
32
|
+
this.timers = timers;
|
|
33
|
+
this.sensorHandler = sensorHandler;
|
|
33
34
|
}
|
|
34
35
|
// ── Public API ─────────────────────────────────────────────────────────────
|
|
35
36
|
setCurrentState(state, origin) {
|
|
@@ -67,66 +68,36 @@ export class StateHandler {
|
|
|
67
68
|
this.state.isArming = true;
|
|
68
69
|
this.handleArmingState();
|
|
69
70
|
this.log.info(`Arm delay (${armSeconds}s)`);
|
|
70
|
-
this.
|
|
71
|
-
this.state.armTimeout = null;
|
|
71
|
+
this.timers.setArmTimer(armSeconds * 1000, () => {
|
|
72
72
|
this.state.isArming = false;
|
|
73
73
|
this.setCurrentState(state, origin);
|
|
74
|
-
}
|
|
74
|
+
});
|
|
75
75
|
return true;
|
|
76
76
|
}
|
|
77
|
-
getArmingSeconds(
|
|
78
|
-
|
|
79
|
-
const isOff = state === SecurityState.OFF;
|
|
80
|
-
if (isTriggered || isOff) {
|
|
81
|
-
return 0;
|
|
82
|
-
}
|
|
83
|
-
if (state === SecurityState.HOME && this.options.homeArmSeconds !== null) {
|
|
84
|
-
return this.options.homeArmSeconds;
|
|
85
|
-
}
|
|
86
|
-
if (state === SecurityState.AWAY && this.options.awayArmSeconds !== null) {
|
|
87
|
-
return this.options.awayArmSeconds;
|
|
88
|
-
}
|
|
89
|
-
if (state === SecurityState.NIGHT && this.options.nightArmSeconds !== null) {
|
|
90
|
-
return this.options.nightArmSeconds;
|
|
91
|
-
}
|
|
92
|
-
return this.options.armSeconds;
|
|
77
|
+
getArmingSeconds(targetState) {
|
|
78
|
+
return getArmingSeconds(this.state, this.options, targetState);
|
|
93
79
|
}
|
|
94
80
|
resetTimers() {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (this.state.triggeredMotionSensorInterval) {
|
|
106
|
-
clearInterval(this.state.triggeredMotionSensorInterval);
|
|
107
|
-
this.state.triggeredMotionSensorInterval = null;
|
|
108
|
-
this.log.debug('Triggered interval (Cleared)');
|
|
109
|
-
}
|
|
110
|
-
if (this.state.trippedMotionSensorInterval) {
|
|
111
|
-
clearInterval(this.state.trippedMotionSensorInterval);
|
|
112
|
-
this.state.trippedMotionSensorInterval = null;
|
|
113
|
-
this.log.debug('Tripped interval (Cleared)');
|
|
114
|
-
}
|
|
115
|
-
if (this.state.doubleKnockTimeout) {
|
|
116
|
-
clearTimeout(this.state.doubleKnockTimeout);
|
|
117
|
-
this.state.doubleKnockTimeout = null;
|
|
118
|
-
this.log.debug('Double-knock timeout (Cleared)');
|
|
119
|
-
}
|
|
120
|
-
if (this.state.pauseTimeout) {
|
|
121
|
-
clearTimeout(this.state.pauseTimeout);
|
|
122
|
-
this.state.pauseTimeout = null;
|
|
123
|
-
this.log.debug('Pause timeout (Cleared)');
|
|
124
|
-
}
|
|
125
|
-
if (this.state.resetTimeout) {
|
|
126
|
-
clearTimeout(this.state.resetTimeout);
|
|
127
|
-
this.state.resetTimeout = null;
|
|
128
|
-
this.log.debug('Reset timeout (Cleared)');
|
|
81
|
+
this.timers.clearAll();
|
|
82
|
+
}
|
|
83
|
+
/** Returns true while the trigger delay is counting down (trip switch is active). */
|
|
84
|
+
isTripping() {
|
|
85
|
+
return this.state.isTripping;
|
|
86
|
+
}
|
|
87
|
+
/** Checks whether arming is currently blocked by an arming-lock switch. */
|
|
88
|
+
isArmingLocked(targetState) {
|
|
89
|
+
if (this.services.armingLockSwitchService.getCharacteristic(this.Characteristic.On).value) {
|
|
90
|
+
return true;
|
|
129
91
|
}
|
|
92
|
+
const modeMap = {
|
|
93
|
+
[SecurityState.HOME]: 'armingLockHomeSwitchService',
|
|
94
|
+
[SecurityState.AWAY]: 'armingLockAwaySwitchService',
|
|
95
|
+
[SecurityState.NIGHT]: 'armingLockNightSwitchService',
|
|
96
|
+
};
|
|
97
|
+
const svcKey = modeMap[targetState];
|
|
98
|
+
return svcKey
|
|
99
|
+
? Boolean(this.services[svcKey].getCharacteristic(this.Characteristic.On).value)
|
|
100
|
+
: false;
|
|
130
101
|
}
|
|
131
102
|
getAvailableTargetStates() {
|
|
132
103
|
const all = [SecurityState.HOME, SecurityState.AWAY, SecurityState.NIGHT, SecurityState.OFF];
|
|
@@ -150,7 +121,7 @@ export class StateHandler {
|
|
|
150
121
|
return true;
|
|
151
122
|
}
|
|
152
123
|
const hasLock = this.options.armingLockSwitch || this.options.armingLockSwitches;
|
|
153
|
-
if (state !== SecurityState.OFF && hasLock && this.
|
|
124
|
+
if (state !== SecurityState.OFF && hasLock && this.isArmingLocked(state)) {
|
|
154
125
|
this.log.warn('Arming lock (Not allowed)');
|
|
155
126
|
return true;
|
|
156
127
|
}
|
|
@@ -158,10 +129,11 @@ export class StateHandler {
|
|
|
158
129
|
}
|
|
159
130
|
handleTargetStateChange(origin) {
|
|
160
131
|
this.resetTimers();
|
|
161
|
-
|
|
132
|
+
// Notify handlers to reset their displayed state (bus is synchronous).
|
|
133
|
+
this.bus.emit(EventType.RESET_TRIP_SWITCHES, {});
|
|
162
134
|
this.sensorHandler.resetTrippedMotionSensor();
|
|
163
|
-
this.
|
|
164
|
-
this.
|
|
135
|
+
this.bus.emit(EventType.RESET_MODE_SWITCHES, {});
|
|
136
|
+
this.bus.emit(EventType.UPDATE_MODE_SWITCHES, {});
|
|
165
137
|
this.bus.emit(EventType.TARGET_CHANGED, { state: this.state.targetState, origin });
|
|
166
138
|
if (this.state.currentState === SecurityState.TRIGGERED) {
|
|
167
139
|
this.sensorHandler.pulseResetMotionSensor();
|
|
@@ -175,19 +147,16 @@ export class StateHandler {
|
|
|
175
147
|
return;
|
|
176
148
|
}
|
|
177
149
|
}
|
|
178
|
-
|
|
150
|
+
// Notify TripHandler to reset trip switches on any state change.
|
|
151
|
+
this.bus.emit(EventType.RESET_TRIP_SWITCHES, {});
|
|
179
152
|
this.bus.emit(EventType.CURRENT_CHANGED, { state: this.state.currentState, origin });
|
|
180
153
|
}
|
|
181
154
|
handleTriggeredState() {
|
|
182
|
-
|
|
183
|
-
clearInterval(this.state.trippedMotionSensorInterval);
|
|
184
|
-
this.state.trippedMotionSensorInterval = null;
|
|
185
|
-
}
|
|
155
|
+
this.timers.clearTrippedInterval();
|
|
186
156
|
if (this.options.triggeredMotionSensor) {
|
|
187
|
-
this.
|
|
157
|
+
this.timers.setTriggeredInterval(this.options.triggeredMotionSensorSeconds * 1000, () => this.sensorHandler.pulseTriggeredMotionSensor());
|
|
188
158
|
}
|
|
189
|
-
this.
|
|
190
|
-
this.state.resetTimeout = null;
|
|
159
|
+
this.timers.setResetTimer(this.options.resetMinutes * 60 * 1000, () => {
|
|
191
160
|
this.log.info('Reset (Finished)');
|
|
192
161
|
this.sensorHandler.pulseResetMotionSensor();
|
|
193
162
|
if (this.options.resetOffFlow) {
|
|
@@ -196,7 +165,7 @@ export class StateHandler {
|
|
|
196
165
|
else {
|
|
197
166
|
this.setCurrentState(this.state.targetState, OriginType.EXTERNAL);
|
|
198
167
|
}
|
|
199
|
-
}
|
|
168
|
+
});
|
|
200
169
|
}
|
|
201
170
|
handleArmingState() {
|
|
202
171
|
this.sensorHandler.updateArmingMotionSensor(true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-handler.js","sourceRoot":"","sources":["../../src/handlers/state-handler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAKrD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"state-handler.js","sourceRoot":"","sources":["../../src/handlers/state-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,22 +5,29 @@ import type { ServiceRegistry } from '../interfaces/service-registry-interface.j
|
|
|
5
5
|
import type { SystemState } from '../interfaces/system-state-interface.js';
|
|
6
6
|
import type { SecuritySystemOptions } from '../interfaces/options-interface.js';
|
|
7
7
|
import type { StateHandler } from './state-handler.js';
|
|
8
|
-
|
|
8
|
+
import type { TimerManager } from '../timers/timer-manager.js';
|
|
9
|
+
import type { EventBusService } from '../services/event-bus-service.js';
|
|
10
|
+
/**
|
|
11
|
+
* Handles all mode switches and the pause/extended switches.
|
|
12
|
+
* Calls StateHandler directly (one-way dependency — no cycle).
|
|
13
|
+
* Subscribes to bus events emitted by StateHandler to reset its own display state.
|
|
14
|
+
*/
|
|
9
15
|
export declare class SwitchHandler {
|
|
10
16
|
private readonly services;
|
|
11
17
|
private readonly state;
|
|
12
18
|
private readonly options;
|
|
13
19
|
private readonly Characteristic;
|
|
14
20
|
private readonly log;
|
|
15
|
-
private
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
private readonly timers;
|
|
22
|
+
private readonly stateHandler;
|
|
23
|
+
constructor(services: ServiceRegistry, state: SystemState, options: SecuritySystemOptions, Characteristic: CharacteristicConstructor, log: Logging, timers: TimerManager, stateHandler: StateHandler);
|
|
24
|
+
/** Register bus listeners so StateHandler can signal display resets without importing this class. */
|
|
25
|
+
subscribeToStateEvents(bus: EventBusService): void;
|
|
18
26
|
setModeSwitch(mode: SecurityState, value: boolean): number | null;
|
|
19
27
|
setModeOffSwitch(value: boolean): number | null;
|
|
20
28
|
setModeAwayExtendedSwitch(value: boolean): number | null;
|
|
21
29
|
setModePauseSwitch(value: boolean): number | null;
|
|
22
30
|
updateArmingLock(mode: string, value: boolean): boolean;
|
|
23
|
-
isArmingLocked(targetState: SecurityState): boolean;
|
|
24
31
|
resetModeSwitches(): void;
|
|
25
32
|
updateModeSwitches(): void;
|
|
26
33
|
private logArmingLock;
|
|
@@ -2,23 +2,33 @@ import { SecurityState } from '../types/security-state-type.js';
|
|
|
2
2
|
import { HK_NOT_ALLOWED_IN_CURRENT_STATE } from '../constants/homekit-constant.js';
|
|
3
3
|
import { OriginType } from '../types/origin-type.js';
|
|
4
4
|
import { capitalise } from '../utils/state-util.js';
|
|
5
|
-
|
|
5
|
+
import { EventType } from '../types/event-type.js';
|
|
6
|
+
/**
|
|
7
|
+
* Handles all mode switches and the pause/extended switches.
|
|
8
|
+
* Calls StateHandler directly (one-way dependency — no cycle).
|
|
9
|
+
* Subscribes to bus events emitted by StateHandler to reset its own display state.
|
|
10
|
+
*/
|
|
6
11
|
export class SwitchHandler {
|
|
7
12
|
services;
|
|
8
13
|
state;
|
|
9
14
|
options;
|
|
10
15
|
Characteristic;
|
|
11
16
|
log;
|
|
17
|
+
timers;
|
|
12
18
|
stateHandler;
|
|
13
|
-
constructor(services, state, options, Characteristic, log) {
|
|
19
|
+
constructor(services, state, options, Characteristic, log, timers, stateHandler) {
|
|
14
20
|
this.services = services;
|
|
15
21
|
this.state = state;
|
|
16
22
|
this.options = options;
|
|
17
23
|
this.Characteristic = Characteristic;
|
|
18
24
|
this.log = log;
|
|
25
|
+
this.timers = timers;
|
|
26
|
+
this.stateHandler = stateHandler;
|
|
19
27
|
}
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
/** Register bus listeners so StateHandler can signal display resets without importing this class. */
|
|
29
|
+
subscribeToStateEvents(bus) {
|
|
30
|
+
bus.on(EventType.RESET_MODE_SWITCHES, () => this.resetModeSwitches());
|
|
31
|
+
bus.on(EventType.UPDATE_MODE_SWITCHES, () => this.updateModeSwitches());
|
|
22
32
|
}
|
|
23
33
|
// ── Mode switches ──────────────────────────────────────────────────────────
|
|
24
34
|
setModeSwitch(mode, value) {
|
|
@@ -58,19 +68,16 @@ export class SwitchHandler {
|
|
|
58
68
|
this.state.pausedCurrentState = this.state.currentState;
|
|
59
69
|
this.stateHandler.updateTargetState(SecurityState.OFF, OriginType.INTERNAL, 0);
|
|
60
70
|
if (this.options.pauseMinutes !== 0) {
|
|
61
|
-
this.
|
|
71
|
+
this.timers.setPauseTimer(this.options.pauseMinutes * 60 * 1000, () => {
|
|
62
72
|
this.log.info('Mode pause (Finished)');
|
|
63
73
|
const prev = this.state.pausedCurrentState ?? this.state.defaultState;
|
|
64
74
|
this.stateHandler.updateTargetState(prev, OriginType.INTERNAL, this.stateHandler.getArmingSeconds(prev));
|
|
65
|
-
}
|
|
75
|
+
});
|
|
66
76
|
}
|
|
67
77
|
}
|
|
68
78
|
else {
|
|
69
79
|
this.log.info('Mode pause (Cancelled)');
|
|
70
|
-
|
|
71
|
-
clearTimeout(this.state.pauseTimeout);
|
|
72
|
-
this.state.pauseTimeout = null;
|
|
73
|
-
}
|
|
80
|
+
this.timers.clearPauseTimer();
|
|
74
81
|
const prev = this.state.pausedCurrentState ?? this.state.defaultState;
|
|
75
82
|
this.stateHandler.updateTargetState(prev, OriginType.INTERNAL, this.stateHandler.getArmingSeconds(prev));
|
|
76
83
|
}
|
|
@@ -93,18 +100,6 @@ export class SwitchHandler {
|
|
|
93
100
|
this.services[key].getCharacteristic(this.Characteristic.On).updateValue(value);
|
|
94
101
|
return true;
|
|
95
102
|
}
|
|
96
|
-
isArmingLocked(targetState) {
|
|
97
|
-
if (this.services.armingLockSwitchService.getCharacteristic(this.Characteristic.On).value) {
|
|
98
|
-
return true;
|
|
99
|
-
}
|
|
100
|
-
const modeMap = {
|
|
101
|
-
[SecurityState.HOME]: 'armingLockHomeSwitchService',
|
|
102
|
-
[SecurityState.AWAY]: 'armingLockAwaySwitchService',
|
|
103
|
-
[SecurityState.NIGHT]: 'armingLockNightSwitchService',
|
|
104
|
-
};
|
|
105
|
-
const svcKey = modeMap[targetState];
|
|
106
|
-
return svcKey ? Boolean(this.services[svcKey].getCharacteristic(this.Characteristic.On).value) : false;
|
|
107
|
-
}
|
|
108
103
|
// ── Mode switch display ────────────────────────────────────────────────────
|
|
109
104
|
resetModeSwitches() {
|
|
110
105
|
const switches = [
|