homebridge-securitysystem 9.1.2 → 10.0.0-beta.3

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 (183) hide show
  1. package/.github/workflows/publish.yml +3 -0
  2. package/AGENTS.md +214 -0
  3. package/dist/conditions/already-triggered-condition.d.ts +10 -0
  4. package/dist/conditions/already-triggered-condition.js +20 -0
  5. package/dist/conditions/already-triggered-condition.js.map +1 -0
  6. package/dist/conditions/arming-in-progress-condition.d.ts +10 -0
  7. package/dist/conditions/arming-in-progress-condition.js +19 -0
  8. package/dist/conditions/arming-in-progress-condition.js.map +1 -0
  9. package/dist/conditions/arming-lock-condition.d.ts +13 -0
  10. package/dist/conditions/arming-lock-condition.js +42 -0
  11. package/dist/conditions/arming-lock-condition.js.map +1 -0
  12. package/dist/conditions/condition.d.ts +15 -0
  13. package/dist/conditions/condition.js +9 -0
  14. package/dist/conditions/condition.js.map +1 -0
  15. package/dist/conditions/double-knock-condition.d.ts +24 -0
  16. package/dist/conditions/double-knock-condition.js +71 -0
  17. package/dist/conditions/double-knock-condition.js.map +1 -0
  18. package/dist/conditions/not-armed-condition.d.ts +10 -0
  19. package/dist/conditions/not-armed-condition.js +24 -0
  20. package/dist/conditions/not-armed-condition.js.map +1 -0
  21. package/dist/conditions/trigger-already-running-condition.d.ts +10 -0
  22. package/dist/conditions/trigger-already-running-condition.js +19 -0
  23. package/dist/conditions/trigger-already-running-condition.js.map +1 -0
  24. package/dist/constants/default-constant.d.ts +26 -0
  25. package/dist/constants/default-constant.js +28 -0
  26. package/dist/constants/default-constant.js.map +1 -0
  27. package/dist/constants/homekit-constant.d.ts +4 -0
  28. package/dist/constants/homekit-constant.js +5 -0
  29. package/dist/constants/homekit-constant.js.map +1 -0
  30. package/dist/constants/switch-uuid-constant.d.ts +23 -0
  31. package/dist/constants/switch-uuid-constant.js +24 -0
  32. package/dist/constants/switch-uuid-constant.js.map +1 -0
  33. package/dist/handlers/sensor-handler.d.ts +17 -0
  34. package/dist/handlers/sensor-handler.js +53 -0
  35. package/dist/handlers/sensor-handler.js.map +1 -0
  36. package/dist/handlers/state-handler.d.ts +46 -0
  37. package/dist/handlers/state-handler.js +182 -0
  38. package/dist/handlers/state-handler.js.map +1 -0
  39. package/dist/handlers/switch-handler.d.ts +34 -0
  40. package/dist/handlers/switch-handler.js +134 -0
  41. package/dist/handlers/switch-handler.js.map +1 -0
  42. package/dist/handlers/trip-handler.d.ts +43 -0
  43. package/dist/handlers/trip-handler.js +158 -0
  44. package/dist/handlers/trip-handler.js.map +1 -0
  45. package/dist/homekit/homekit-registrar.d.ts +19 -0
  46. package/dist/homekit/homekit-registrar.js +134 -0
  47. package/dist/homekit/homekit-registrar.js.map +1 -0
  48. package/dist/homekit/service-factory.d.ts +9 -0
  49. package/dist/homekit/service-factory.js +117 -0
  50. package/dist/homekit/service-factory.js.map +1 -0
  51. package/dist/index.d.ts +3 -0
  52. package/dist/index.js +7 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/interfaces/condition-context-interface.d.ts +16 -0
  55. package/dist/interfaces/condition-context-interface.js +2 -0
  56. package/dist/interfaces/condition-context-interface.js.map +1 -0
  57. package/dist/interfaces/hap-types-interface.d.ts +9 -0
  58. package/dist/interfaces/hap-types-interface.js +2 -0
  59. package/dist/interfaces/hap-types-interface.js.map +1 -0
  60. package/dist/interfaces/options-interface.d.ts +93 -0
  61. package/dist/interfaces/options-interface.js +2 -0
  62. package/dist/interfaces/options-interface.js.map +1 -0
  63. package/dist/interfaces/persisted-state-interface.d.ts +6 -0
  64. package/dist/interfaces/persisted-state-interface.js +2 -0
  65. package/dist/interfaces/persisted-state-interface.js.map +1 -0
  66. package/dist/interfaces/service-registry-interface.d.ts +26 -0
  67. package/dist/interfaces/service-registry-interface.js +2 -0
  68. package/dist/interfaces/service-registry-interface.js.map +1 -0
  69. package/dist/interfaces/system-state-interface.d.ts +15 -0
  70. package/dist/interfaces/system-state-interface.js +2 -0
  71. package/dist/interfaces/system-state-interface.js.map +1 -0
  72. package/dist/security-system.d.ts +22 -0
  73. package/dist/security-system.js +147 -0
  74. package/dist/security-system.js.map +1 -0
  75. package/dist/services/audio-service.d.ts +21 -0
  76. package/dist/services/audio-service.js +113 -0
  77. package/dist/services/audio-service.js.map +1 -0
  78. package/dist/services/command-service.d.ts +16 -0
  79. package/dist/services/command-service.js +65 -0
  80. package/dist/services/command-service.js.map +1 -0
  81. package/dist/services/configuration-service.d.ts +24 -0
  82. package/dist/services/configuration-service.js +187 -0
  83. package/dist/services/configuration-service.js.map +1 -0
  84. package/dist/services/event-bus-service.d.ts +12 -0
  85. package/dist/services/event-bus-service.js +17 -0
  86. package/dist/services/event-bus-service.js.map +1 -0
  87. package/dist/services/server-service.d.ts +25 -0
  88. package/dist/services/server-service.js +140 -0
  89. package/dist/services/server-service.js.map +1 -0
  90. package/dist/services/storage-service.d.ts +14 -0
  91. package/dist/services/storage-service.js +70 -0
  92. package/dist/services/storage-service.js.map +1 -0
  93. package/dist/services/webhook-service.d.ts +16 -0
  94. package/dist/services/webhook-service.js +73 -0
  95. package/dist/services/webhook-service.js.map +1 -0
  96. package/dist/tests/conditions.test.d.ts +1 -0
  97. package/dist/tests/conditions.test.js +148 -0
  98. package/dist/tests/conditions.test.js.map +1 -0
  99. package/dist/tests/state-handler.test.d.ts +1 -0
  100. package/dist/tests/state-handler.test.js +168 -0
  101. package/dist/tests/state-handler.test.js.map +1 -0
  102. package/dist/tests/trip-handler.test.d.ts +1 -0
  103. package/dist/tests/trip-handler.test.js +169 -0
  104. package/dist/tests/trip-handler.test.js.map +1 -0
  105. package/dist/timers/timer-manager.d.ts +34 -0
  106. package/dist/timers/timer-manager.js +132 -0
  107. package/dist/timers/timer-manager.js.map +1 -0
  108. package/dist/types/event-payload-map-type.d.ts +14 -0
  109. package/dist/types/event-payload-map-type.js +2 -0
  110. package/dist/types/event-payload-map-type.js.map +1 -0
  111. package/dist/types/event-type.d.ts +38 -0
  112. package/dist/types/event-type.js +17 -0
  113. package/dist/types/event-type.js.map +1 -0
  114. package/dist/types/mode-type.d.ts +4 -0
  115. package/dist/types/mode-type.js +2 -0
  116. package/dist/types/mode-type.js.map +1 -0
  117. package/dist/types/origin-type.d.ts +9 -0
  118. package/dist/types/origin-type.js +11 -0
  119. package/dist/types/origin-type.js.map +1 -0
  120. package/dist/types/security-state-type.d.ts +11 -0
  121. package/dist/types/security-state-type.js +13 -0
  122. package/dist/types/security-state-type.js.map +1 -0
  123. package/dist/utils/arming-util.d.ts +19 -0
  124. package/dist/utils/arming-util.js +24 -0
  125. package/dist/utils/arming-util.js.map +1 -0
  126. package/dist/utils/log-util.d.ts +4 -0
  127. package/dist/utils/log-util.js +47 -0
  128. package/dist/utils/log-util.js.map +1 -0
  129. package/dist/utils/state-util.d.ts +8 -0
  130. package/dist/utils/state-util.js +29 -0
  131. package/dist/utils/state-util.js.map +1 -0
  132. package/eslint.config.js +35 -0
  133. package/package.json +40 -34
  134. package/src/@types/homebridge-lib.d.ts +14 -0
  135. package/src/conditions/already-triggered-condition.ts +22 -0
  136. package/src/conditions/arming-in-progress-condition.ts +21 -0
  137. package/src/conditions/arming-lock-condition.ts +48 -0
  138. package/src/conditions/condition.ts +17 -0
  139. package/src/conditions/double-knock-condition.ts +82 -0
  140. package/src/conditions/not-armed-condition.ts +28 -0
  141. package/src/conditions/trigger-already-running-condition.ts +21 -0
  142. package/src/constants/default-constant.ts +28 -0
  143. package/src/constants/homekit-constant.ts +5 -0
  144. package/src/constants/switch-uuid-constant.ts +23 -0
  145. package/src/handlers/sensor-handler.ts +81 -0
  146. package/src/handlers/state-handler.ts +234 -0
  147. package/src/handlers/switch-handler.ts +156 -0
  148. package/src/handlers/trip-handler.ts +195 -0
  149. package/src/homekit/homekit-registrar.ts +147 -0
  150. package/src/homekit/service-factory.ts +139 -0
  151. package/src/index.ts +9 -0
  152. package/src/interfaces/condition-context-interface.ts +17 -0
  153. package/src/interfaces/hap-types-interface.ts +11 -0
  154. package/src/interfaces/options-interface.ts +120 -0
  155. package/src/interfaces/persisted-state-interface.ts +7 -0
  156. package/src/interfaces/service-registry-interface.ts +37 -0
  157. package/src/interfaces/system-state-interface.ts +17 -0
  158. package/src/security-system.ts +179 -0
  159. package/src/services/audio-service.ts +133 -0
  160. package/src/services/command-service.ts +76 -0
  161. package/src/services/configuration-service.ts +220 -0
  162. package/src/services/event-bus-service.ts +21 -0
  163. package/src/services/server-service.ts +176 -0
  164. package/src/services/storage-service.ts +80 -0
  165. package/src/services/webhook-service.ts +83 -0
  166. package/src/tests/conditions.test.ts +178 -0
  167. package/src/tests/state-handler.test.ts +198 -0
  168. package/src/tests/trip-handler.test.ts +197 -0
  169. package/src/timers/timer-manager.ts +158 -0
  170. package/src/types/event-payload-map-type.ts +23 -0
  171. package/src/types/event-type.ts +51 -0
  172. package/src/types/mode-type.ts +4 -0
  173. package/src/types/origin-type.ts +9 -0
  174. package/src/types/security-state-type.ts +11 -0
  175. package/src/utils/arming-util.ts +44 -0
  176. package/src/utils/log-util.ts +54 -0
  177. package/src/utils/state-util.ts +32 -0
  178. package/tsconfig.json +25 -0
  179. package/vitest.config.ts +8 -0
  180. package/.eslintrc.js +0 -16
  181. package/.vscode/settings.json +0 -12
  182. package/src/index.js +0 -2525
  183. package/src/utils/options.js +0 -409
@@ -0,0 +1,23 @@
1
+ /** Stable subtype UUIDs for each HomeKit service. Must not change between restarts. */
2
+ export declare const SWITCH_UUIDS: {
3
+ readonly TRIP: "siren-switch";
4
+ readonly TRIP_HOME: "siren-home";
5
+ readonly TRIP_AWAY: "siren-away";
6
+ readonly TRIP_NIGHT: "siren-night";
7
+ readonly TRIP_OVERRIDE: "pink-sheep";
8
+ readonly ARMING_LOCK: "arming-lock";
9
+ readonly ARMING_LOCK_HOME: "arming-lock-home";
10
+ readonly ARMING_LOCK_AWAY: "arming-lock-away";
11
+ readonly ARMING_LOCK_NIGHT: "arming-lock-night";
12
+ readonly MODE_HOME: "mode-home";
13
+ readonly MODE_AWAY: "mode-away";
14
+ readonly MODE_NIGHT: "mode-night";
15
+ readonly MODE_OFF: "mode-off";
16
+ readonly MODE_AWAY_EXTENDED: "mode-away-extended";
17
+ readonly MODE_PAUSE: "mode-pause";
18
+ readonly AUDIO: "kx82r64zN3txDXKFiX9JDi";
19
+ readonly ARMING_SENSOR: "arming-motion-sensor";
20
+ readonly TRIPPED_SENSOR: "siren-tripped";
21
+ readonly TRIGGERED_SENSOR: "siren-triggered";
22
+ readonly RESET_SENSOR: "reset-event";
23
+ };
@@ -0,0 +1,24 @@
1
+ /** Stable subtype UUIDs for each HomeKit service. Must not change between restarts. */
2
+ export const SWITCH_UUIDS = {
3
+ TRIP: 'siren-switch',
4
+ TRIP_HOME: 'siren-home',
5
+ TRIP_AWAY: 'siren-away',
6
+ TRIP_NIGHT: 'siren-night',
7
+ TRIP_OVERRIDE: 'pink-sheep',
8
+ ARMING_LOCK: 'arming-lock',
9
+ ARMING_LOCK_HOME: 'arming-lock-home',
10
+ ARMING_LOCK_AWAY: 'arming-lock-away',
11
+ ARMING_LOCK_NIGHT: 'arming-lock-night',
12
+ MODE_HOME: 'mode-home',
13
+ MODE_AWAY: 'mode-away',
14
+ MODE_NIGHT: 'mode-night',
15
+ MODE_OFF: 'mode-off',
16
+ MODE_AWAY_EXTENDED: 'mode-away-extended',
17
+ MODE_PAUSE: 'mode-pause',
18
+ AUDIO: 'kx82r64zN3txDXKFiX9JDi',
19
+ ARMING_SENSOR: 'arming-motion-sensor',
20
+ TRIPPED_SENSOR: 'siren-tripped',
21
+ TRIGGERED_SENSOR: 'siren-triggered',
22
+ RESET_SENSOR: 'reset-event',
23
+ };
24
+ //# sourceMappingURL=switch-uuid-constant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"switch-uuid-constant.js","sourceRoot":"","sources":["../../src/constants/switch-uuid-constant.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,cAAc;IACpB,SAAS,EAAE,YAAY;IACvB,SAAS,EAAE,YAAY;IACvB,UAAU,EAAE,aAAa;IACzB,aAAa,EAAE,YAAY;IAC3B,WAAW,EAAE,aAAa;IAC1B,gBAAgB,EAAE,kBAAkB;IACpC,gBAAgB,EAAE,kBAAkB;IACpC,iBAAiB,EAAE,mBAAmB;IACtC,SAAS,EAAE,WAAW;IACtB,SAAS,EAAE,WAAW;IACtB,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;IACpB,kBAAkB,EAAE,oBAAoB;IACxC,UAAU,EAAE,YAAY;IACxB,KAAK,EAAE,wBAAwB;IAC/B,aAAa,EAAE,sBAAsB;IACrC,cAAc,EAAE,eAAe;IAC/B,gBAAgB,EAAE,iBAAiB;IACnC,YAAY,EAAE,aAAa;CACnB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { Logging } from 'homebridge';
2
+ import type { ServiceRegistry } from '../interfaces/service-registry-interface.js';
3
+ import type { CharacteristicConstructor } from '../interfaces/hap-types-interface.js';
4
+ /** Manages all motion sensor characteristic updates. */
5
+ export declare class SensorHandler {
6
+ private readonly services;
7
+ private readonly Characteristic;
8
+ private readonly log;
9
+ constructor(services: ServiceRegistry, Characteristic: CharacteristicConstructor, log: Logging);
10
+ updateArmingMotionSensor(value: boolean): void;
11
+ resetArmingMotionSensor(): void;
12
+ pulseTrippedMotionSensor(): void;
13
+ resetTrippedMotionSensor(): void;
14
+ pulseTriggeredMotionSensor(): void;
15
+ pulseResetMotionSensor(): void;
16
+ private scheduleReset;
17
+ }
@@ -0,0 +1,53 @@
1
+ import { SENSOR_PULSE_MS } from '../constants/homekit-constant.js';
2
+ /** Manages all motion sensor characteristic updates. */
3
+ export class SensorHandler {
4
+ services;
5
+ Characteristic;
6
+ log;
7
+ constructor(services, Characteristic, log) {
8
+ this.services = services;
9
+ this.Characteristic = Characteristic;
10
+ this.log = log;
11
+ }
12
+ // ── Arming sensor ──────────────────────────────────────────────────────────
13
+ updateArmingMotionSensor(value) {
14
+ this.services.armingMotionSensorService.updateCharacteristic(this.Characteristic.MotionDetected, value);
15
+ }
16
+ resetArmingMotionSensor() {
17
+ const current = this.services.armingMotionSensorService
18
+ .getCharacteristic(this.Characteristic.MotionDetected).value;
19
+ if (current) {
20
+ this.services.armingMotionSensorService.updateCharacteristic(this.Characteristic.MotionDetected, false);
21
+ }
22
+ }
23
+ // ── Tripped sensor ─────────────────────────────────────────────────────────
24
+ pulseTrippedMotionSensor() {
25
+ this.services.trippedMotionSensorService.updateCharacteristic(this.Characteristic.MotionDetected, true);
26
+ this.scheduleReset(this.services.trippedMotionSensorService);
27
+ }
28
+ resetTrippedMotionSensor() {
29
+ const char = this.services.trippedMotionSensorService
30
+ .getCharacteristic(this.Characteristic.MotionDetected);
31
+ if (char.value) {
32
+ char.updateValue(false);
33
+ }
34
+ }
35
+ // ── Triggered sensor ───────────────────────────────────────────────────────
36
+ pulseTriggeredMotionSensor() {
37
+ this.services.triggeredMotionSensorService.updateCharacteristic(this.Characteristic.MotionDetected, true);
38
+ this.scheduleReset(this.services.triggeredMotionSensorService);
39
+ }
40
+ // ── Reset sensor ───────────────────────────────────────────────────────────
41
+ pulseResetMotionSensor() {
42
+ this.services.triggeredResetMotionSensorService.updateCharacteristic(this.Characteristic.MotionDetected, true);
43
+ this.scheduleReset(this.services.triggeredResetMotionSensorService);
44
+ this.log.debug('Reset sensor (Triggered)');
45
+ }
46
+ // ── Private helpers ────────────────────────────────────────────────────────
47
+ scheduleReset(service) {
48
+ setTimeout(() => {
49
+ service.updateCharacteristic(this.Characteristic.MotionDetected, false);
50
+ }, SENSOR_PULSE_MS);
51
+ }
52
+ }
53
+ //# sourceMappingURL=sensor-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensor-handler.js","sourceRoot":"","sources":["../../src/handlers/sensor-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAInE,wDAAwD;AACxD,MAAM,OAAO,aAAa;IAEL;IACA;IACA;IAHnB,YACmB,QAAyB,EACzB,cAAyC,EACzC,GAAY;QAFZ,aAAQ,GAAR,QAAQ,CAAiB;QACzB,mBAAc,GAAd,cAAc,CAA2B;QACzC,QAAG,GAAH,GAAG,CAAS;IAC5B,CAAC;IAEJ,8EAA8E;IAE9E,wBAAwB,CAAC,KAAc;QACrC,IAAI,CAAC,QAAQ,CAAC,yBAAyB,CAAC,oBAAoB,CAC1D,IAAI,CAAC,cAAc,CAAC,cAAc,EAClC,KAAK,CACN,CAAC;IACJ,CAAC;IAED,uBAAuB;QACrB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,yBAAyB;aACpD,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC;QAE/D,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC,QAAQ,CAAC,yBAAyB,CAAC,oBAAoB,CAC1D,IAAI,CAAC,cAAc,CAAC,cAAc,EAClC,KAAK,CACN,CAAC;QACJ,CAAC;IACH,CAAC;IAED,8EAA8E;IAE9E,wBAAwB;QACtB,IAAI,CAAC,QAAQ,CAAC,0BAA0B,CAAC,oBAAoB,CAC3D,IAAI,CAAC,cAAc,CAAC,cAAc,EAClC,IAAI,CACL,CAAC;QACF,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,0BAA0B,CAAC,CAAC;IAC/D,CAAC;IAED,wBAAwB;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,0BAA0B;aAClD,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;QACzD,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,8EAA8E;IAE9E,0BAA0B;QACxB,IAAI,CAAC,QAAQ,CAAC,4BAA4B,CAAC,oBAAoB,CAC7D,IAAI,CAAC,cAAc,CAAC,cAAc,EAClC,IAAI,CACL,CAAC;QACF,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAC;IACjE,CAAC;IAED,8EAA8E;IAE9E,sBAAsB;QACpB,IAAI,CAAC,QAAQ,CAAC,iCAAiC,CAAC,oBAAoB,CAClE,IAAI,CAAC,cAAc,CAAC,cAAc,EAClC,IAAI,CACL,CAAC;QACF,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,iCAAiC,CAAC,CAAC;QACpE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC7C,CAAC;IAED,8EAA8E;IAEtE,aAAa,CAAC,OAA+C;QACnE,UAAU,CAAC,GAAG,EAAE;YACd,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,cAAc,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;QAC1E,CAAC,EAAE,eAAe,CAAC,CAAC;IACtB,CAAC;CACF"}
@@ -0,0 +1,46 @@
1
+ import type { Logging } from 'homebridge';
2
+ import type { CharacteristicConstructor } from '../interfaces/hap-types-interface.js';
3
+ import { SecurityState } from '../types/security-state-type.js';
4
+ import { OriginType } from '../types/origin-type.js';
5
+ import type { ServiceRegistry } from '../interfaces/service-registry-interface.js';
6
+ import type { SystemState } from '../interfaces/system-state-interface.js';
7
+ import type { SecuritySystemOptions } from '../interfaces/options-interface.js';
8
+ import type { EventBusService } from '../services/event-bus-service.js';
9
+ import type { StorageService } from '../services/storage-service.js';
10
+ import type { AudioService } from '../services/audio-service.js';
11
+ import type { SensorHandler } from './sensor-handler.js';
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
+ */
18
+ export declare class StateHandler {
19
+ private readonly services;
20
+ private readonly state;
21
+ private readonly options;
22
+ private readonly Characteristic;
23
+ private readonly log;
24
+ private readonly bus;
25
+ private readonly storageService;
26
+ private readonly audio;
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);
30
+ setCurrentState(state: SecurityState, origin: OriginType): void;
31
+ updateTargetState(state: SecurityState, origin: OriginType, delay: number): boolean;
32
+ getArmingSeconds(targetState: SecurityState): number;
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;
38
+ getAvailableTargetStates(): SecurityState[];
39
+ logMode(type: string, state: SecurityState | string): void;
40
+ private isBadTargetState;
41
+ private handleTargetStateChange;
42
+ private handleCurrentStateChange;
43
+ private handleTriggeredState;
44
+ private handleArmingState;
45
+ private resetViaOffMode;
46
+ }
@@ -0,0 +1,182 @@
1
+ import { SecurityState } from '../types/security-state-type.js';
2
+ import { OriginType } from '../types/origin-type.js';
3
+ import { stateToMode, capitalise } from '../utils/state-util.js';
4
+ import { modeToState } from '../utils/state-util.js';
5
+ import { EventType } from '../types/event-type.js';
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
+ */
12
+ export class StateHandler {
13
+ services;
14
+ state;
15
+ options;
16
+ Characteristic;
17
+ log;
18
+ bus;
19
+ storageService;
20
+ audio;
21
+ timers;
22
+ sensorHandler;
23
+ constructor(services, state, options, Characteristic, log, bus, storageService, audio, timers, sensorHandler) {
24
+ this.services = services;
25
+ this.state = state;
26
+ this.options = options;
27
+ this.Characteristic = Characteristic;
28
+ this.log = log;
29
+ this.bus = bus;
30
+ this.storageService = storageService;
31
+ this.audio = audio;
32
+ this.timers = timers;
33
+ this.sensorHandler = sensorHandler;
34
+ }
35
+ // ── Public API ─────────────────────────────────────────────────────────────
36
+ setCurrentState(state, origin) {
37
+ this.sensorHandler.resetArmingMotionSensor();
38
+ this.audio.play('current', state);
39
+ if (this.state.currentState === state) {
40
+ this.log.warn('Current mode (Already set)');
41
+ return;
42
+ }
43
+ this.state.currentState = state;
44
+ this.services.mainService.setCharacteristic(this.Characteristic.SecuritySystemCurrentState, state);
45
+ this.logMode('Current', state);
46
+ this.handleCurrentStateChange(origin);
47
+ this.storageService.save(this.state);
48
+ }
49
+ updateTargetState(state, origin, delay) {
50
+ if (this.isBadTargetState(state)) {
51
+ return false;
52
+ }
53
+ this.state.targetState = state;
54
+ this.logMode('Target', state);
55
+ if (origin === OriginType.INTERNAL || origin === OriginType.EXTERNAL) {
56
+ this.services.mainService.updateCharacteristic(this.Characteristic.SecuritySystemTargetState, state);
57
+ }
58
+ this.handleTargetStateChange(origin);
59
+ if (state === this.state.currentState) {
60
+ this.setCurrentState(state, origin);
61
+ return false;
62
+ }
63
+ const armSeconds = delay > 0 ? delay : 0;
64
+ if (armSeconds === 0) {
65
+ this.setCurrentState(state, origin);
66
+ return false;
67
+ }
68
+ this.state.isArming = true;
69
+ this.handleArmingState();
70
+ this.log.info(`Arm delay (${armSeconds}s)`);
71
+ this.timers.setArmTimer(armSeconds * 1000, () => {
72
+ this.state.isArming = false;
73
+ this.setCurrentState(state, origin);
74
+ });
75
+ return true;
76
+ }
77
+ getArmingSeconds(targetState) {
78
+ return getArmingSeconds(this.state, this.options, targetState);
79
+ }
80
+ resetTimers() {
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;
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;
101
+ }
102
+ getAvailableTargetStates() {
103
+ const all = [SecurityState.HOME, SecurityState.AWAY, SecurityState.NIGHT, SecurityState.OFF];
104
+ const disabled = this.options.disabledModes.map(m => modeToState(m.toLowerCase()));
105
+ return all.filter(s => !disabled.includes(s));
106
+ }
107
+ logMode(type, state) {
108
+ const mode = capitalise(stateToMode(state));
109
+ this.log.info(`${type} mode (${mode})`);
110
+ }
111
+ // ── Private helpers ────────────────────────────────────────────────────────
112
+ isBadTargetState(state) {
113
+ const isTriggered = this.state.currentState === SecurityState.TRIGGERED;
114
+ const alreadySet = this.state.targetState === state;
115
+ if (alreadySet && !isTriggered) {
116
+ this.log.warn('Target mode (Already set)');
117
+ return true;
118
+ }
119
+ if (!this.state.availableTargetStates.includes(state)) {
120
+ this.log.warn('Target mode (Disabled)');
121
+ return true;
122
+ }
123
+ const hasLock = this.options.armingLockSwitch || this.options.armingLockSwitches;
124
+ if (state !== SecurityState.OFF && hasLock && this.isArmingLocked(state)) {
125
+ this.log.warn('Arming lock (Not allowed)');
126
+ return true;
127
+ }
128
+ return false;
129
+ }
130
+ handleTargetStateChange(origin) {
131
+ this.resetTimers();
132
+ // Notify handlers to reset their displayed state (bus is synchronous).
133
+ this.bus.emit(EventType.RESET_TRIP_SWITCHES, {});
134
+ this.sensorHandler.resetTrippedMotionSensor();
135
+ this.bus.emit(EventType.RESET_MODE_SWITCHES, {});
136
+ this.bus.emit(EventType.UPDATE_MODE_SWITCHES, {});
137
+ this.bus.emit(EventType.TARGET_CHANGED, { state: this.state.targetState, origin });
138
+ if (this.state.currentState === SecurityState.TRIGGERED) {
139
+ this.sensorHandler.pulseResetMotionSensor();
140
+ }
141
+ this.state.isKnocked = false;
142
+ }
143
+ handleCurrentStateChange(origin) {
144
+ if (this.state.currentState === SecurityState.TRIGGERED) {
145
+ this.handleTriggeredState();
146
+ if (this.options.testMode) {
147
+ return;
148
+ }
149
+ }
150
+ // Notify TripHandler to reset trip switches on any state change.
151
+ this.bus.emit(EventType.RESET_TRIP_SWITCHES, {});
152
+ this.bus.emit(EventType.CURRENT_CHANGED, { state: this.state.currentState, origin });
153
+ }
154
+ handleTriggeredState() {
155
+ this.timers.clearTrippedInterval();
156
+ if (this.options.triggeredMotionSensor) {
157
+ this.timers.setTriggeredInterval(this.options.triggeredMotionSensorSeconds * 1000, () => this.sensorHandler.pulseTriggeredMotionSensor());
158
+ }
159
+ this.timers.setResetTimer(this.options.resetMinutes * 60 * 1000, () => {
160
+ this.log.info('Reset (Finished)');
161
+ this.sensorHandler.pulseResetMotionSensor();
162
+ if (this.options.resetOffFlow) {
163
+ this.resetViaOffMode();
164
+ }
165
+ else {
166
+ this.setCurrentState(this.state.targetState, OriginType.EXTERNAL);
167
+ }
168
+ });
169
+ }
170
+ handleArmingState() {
171
+ this.sensorHandler.updateArmingMotionSensor(true);
172
+ this.bus.emit(EventType.ARMING, { state: this.state.targetState });
173
+ }
174
+ resetViaOffMode() {
175
+ const original = this.state.targetState;
176
+ this.updateTargetState(SecurityState.OFF, OriginType.INTERNAL, 0);
177
+ setTimeout(() => {
178
+ this.updateTargetState(original, OriginType.INTERNAL, this.getArmingSeconds(original));
179
+ }, 100);
180
+ }
181
+ }
182
+ //# sourceMappingURL=state-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-handler.js","sourceRoot":"","sources":["../../src/handlers/state-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { Logging } from 'homebridge';
2
+ import type { CharacteristicConstructor } from '../interfaces/hap-types-interface.js';
3
+ import { SecurityState } from '../types/security-state-type.js';
4
+ import type { ServiceRegistry } from '../interfaces/service-registry-interface.js';
5
+ import type { SystemState } from '../interfaces/system-state-interface.js';
6
+ import type { SecuritySystemOptions } from '../interfaces/options-interface.js';
7
+ import type { StateHandler } from './state-handler.js';
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
+ */
15
+ export declare class SwitchHandler {
16
+ private readonly services;
17
+ private readonly state;
18
+ private readonly options;
19
+ private readonly Characteristic;
20
+ private readonly log;
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;
26
+ setModeSwitch(mode: SecurityState, value: boolean): number | null;
27
+ setModeOffSwitch(value: boolean): number | null;
28
+ setModeAwayExtendedSwitch(value: boolean): number | null;
29
+ setModePauseSwitch(value: boolean): number | null;
30
+ updateArmingLock(mode: string, value: boolean): boolean;
31
+ resetModeSwitches(): void;
32
+ updateModeSwitches(): void;
33
+ private logArmingLock;
34
+ }
@@ -0,0 +1,134 @@
1
+ import { SecurityState } from '../types/security-state-type.js';
2
+ import { HK_NOT_ALLOWED_IN_CURRENT_STATE } from '../constants/homekit-constant.js';
3
+ import { OriginType } from '../types/origin-type.js';
4
+ import { capitalise } from '../utils/state-util.js';
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
+ */
11
+ export class SwitchHandler {
12
+ services;
13
+ state;
14
+ options;
15
+ Characteristic;
16
+ log;
17
+ timers;
18
+ stateHandler;
19
+ constructor(services, state, options, Characteristic, log, timers, stateHandler) {
20
+ this.services = services;
21
+ this.state = state;
22
+ this.options = options;
23
+ this.Characteristic = Characteristic;
24
+ this.log = log;
25
+ this.timers = timers;
26
+ this.stateHandler = stateHandler;
27
+ }
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());
32
+ }
33
+ // ── Mode switches ──────────────────────────────────────────────────────────
34
+ setModeSwitch(mode, value) {
35
+ if (!value) {
36
+ return HK_NOT_ALLOWED_IN_CURRENT_STATE;
37
+ }
38
+ const delay = this.stateHandler.getArmingSeconds(mode);
39
+ this.stateHandler.updateTargetState(mode, OriginType.INTERNAL, delay);
40
+ return null;
41
+ }
42
+ setModeOffSwitch(value) {
43
+ if (!value) {
44
+ return HK_NOT_ALLOWED_IN_CURRENT_STATE;
45
+ }
46
+ this.stateHandler.updateTargetState(SecurityState.OFF, OriginType.INTERNAL, 0);
47
+ return null;
48
+ }
49
+ setModeAwayExtendedSwitch(value) {
50
+ if (!value) {
51
+ return HK_NOT_ALLOWED_IN_CURRENT_STATE;
52
+ }
53
+ const delay = this.stateHandler.getArmingSeconds(SecurityState.AWAY);
54
+ this.stateHandler.updateTargetState(SecurityState.AWAY, OriginType.INTERNAL, delay);
55
+ return null;
56
+ }
57
+ setModePauseSwitch(value) {
58
+ if (this.state.currentState === SecurityState.TRIGGERED) {
59
+ this.log.warn('Mode pause (Alarm is triggered)');
60
+ return HK_NOT_ALLOWED_IN_CURRENT_STATE;
61
+ }
62
+ if (value) {
63
+ if (this.state.currentState === SecurityState.OFF) {
64
+ this.log.warn('Mode pause (Not armed)');
65
+ return HK_NOT_ALLOWED_IN_CURRENT_STATE;
66
+ }
67
+ this.log.info('Mode pause (Started)');
68
+ this.state.pausedCurrentState = this.state.currentState;
69
+ this.stateHandler.updateTargetState(SecurityState.OFF, OriginType.INTERNAL, 0);
70
+ if (this.options.pauseMinutes !== 0) {
71
+ this.timers.setPauseTimer(this.options.pauseMinutes * 60 * 1000, () => {
72
+ this.log.info('Mode pause (Finished)');
73
+ const prev = this.state.pausedCurrentState ?? this.state.defaultState;
74
+ this.stateHandler.updateTargetState(prev, OriginType.INTERNAL, this.stateHandler.getArmingSeconds(prev));
75
+ });
76
+ }
77
+ }
78
+ else {
79
+ this.log.info('Mode pause (Cancelled)');
80
+ this.timers.clearPauseTimer();
81
+ const prev = this.state.pausedCurrentState ?? this.state.defaultState;
82
+ this.stateHandler.updateTargetState(prev, OriginType.INTERNAL, this.stateHandler.getArmingSeconds(prev));
83
+ }
84
+ return null;
85
+ }
86
+ // ── Arming lock switches ───────────────────────────────────────────────────
87
+ updateArmingLock(mode, value) {
88
+ this.logArmingLock(mode, value);
89
+ const map = {
90
+ global: 'armingLockSwitchService',
91
+ home: 'armingLockHomeSwitchService',
92
+ away: 'armingLockAwaySwitchService',
93
+ night: 'armingLockNightSwitchService',
94
+ };
95
+ const key = map[mode];
96
+ if (!key) {
97
+ this.log.debug(`Unknown arming lock mode (${mode})`);
98
+ return false;
99
+ }
100
+ this.services[key].getCharacteristic(this.Characteristic.On).updateValue(value);
101
+ return true;
102
+ }
103
+ // ── Mode switch display ────────────────────────────────────────────────────
104
+ resetModeSwitches() {
105
+ const switches = [
106
+ 'modeHomeSwitchService', 'modeAwaySwitchService', 'modeNightSwitchService',
107
+ 'modeOffSwitchService', 'modeAwayExtendedSwitchService', 'modePauseSwitchService',
108
+ ];
109
+ for (const key of switches) {
110
+ const char = this.services[key].getCharacteristic(this.Characteristic.On);
111
+ if (char.value) {
112
+ char.updateValue(false);
113
+ this.log.debug(`${key} (Off)`);
114
+ }
115
+ }
116
+ }
117
+ updateModeSwitches() {
118
+ const modeMap = {
119
+ [SecurityState.HOME]: 'modeHomeSwitchService',
120
+ [SecurityState.AWAY]: 'modeAwaySwitchService',
121
+ [SecurityState.NIGHT]: 'modeNightSwitchService',
122
+ [SecurityState.OFF]: 'modeOffSwitchService',
123
+ };
124
+ const key = modeMap[this.state.targetState];
125
+ if (key) {
126
+ this.services[key].updateCharacteristic(this.Characteristic.On, true);
127
+ this.log.debug(`${key} (On)`);
128
+ }
129
+ }
130
+ logArmingLock(mode, value) {
131
+ this.log.info(`Arming lock [${capitalise(mode)}] (${value ? 'On' : 'Off'})`);
132
+ }
133
+ }
134
+ //# sourceMappingURL=switch-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"switch-handler.js","sourceRoot":"","sources":["../../src/handlers/switch-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import type { Logging } from 'homebridge';
2
+ import type { CharacteristicConstructor } from '../interfaces/hap-types-interface.js';
3
+ import { SecurityState } from '../types/security-state-type.js';
4
+ import { OriginType } from '../types/origin-type.js';
5
+ import type { ServiceRegistry } from '../interfaces/service-registry-interface.js';
6
+ import type { SystemState } from '../interfaces/system-state-interface.js';
7
+ import type { SecuritySystemOptions } from '../interfaces/options-interface.js';
8
+ import type { EventBusService } from '../services/event-bus-service.js';
9
+ import type { AudioService } from '../services/audio-service.js';
10
+ import type { SensorHandler } from './sensor-handler.js';
11
+ import type { TimerManager } from '../timers/timer-manager.js';
12
+ /**
13
+ * Handles the trip switch and trigger-delay logic, including all blocking conditions.
14
+ * Communicates state transitions back to the state machine via the event bus so that
15
+ * no circular import is needed with StateHandler.
16
+ */
17
+ export declare class TripHandler {
18
+ private readonly services;
19
+ private readonly state;
20
+ private readonly options;
21
+ private readonly Characteristic;
22
+ private readonly log;
23
+ private readonly bus;
24
+ private readonly audio;
25
+ private readonly sensorHandler;
26
+ private readonly timers;
27
+ private readonly conditions;
28
+ constructor(services: ServiceRegistry, state: SystemState, options: SecuritySystemOptions, Characteristic: CharacteristicConstructor, log: Logging, bus: EventBusService, audio: AudioService, sensorHandler: SensorHandler, timers: TimerManager);
29
+ /**
30
+ * Core trip-switch logic shared by all trip/trigger paths.
31
+ * Returns `true` on success, `false` if blocked.
32
+ */
33
+ updateTripSwitch(value: boolean, origin: OriginType, stateChanged: boolean): boolean;
34
+ /**
35
+ * Trip a mode-specific switch. Only triggers if the system is currently in
36
+ * the required mode (or the alarm is triggered and target matches).
37
+ */
38
+ triggerIfModeSet(requiredState: SecurityState, value: boolean): boolean;
39
+ resetTripSwitches(): void;
40
+ private activateTrip;
41
+ private cancelTrip;
42
+ private resolveTriggerSeconds;
43
+ }