homebridge-securitysystem 9.1.2 → 10.0.0-beta.2

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 (163) 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 +16 -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 +15 -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 +22 -0
  16. package/dist/conditions/double-knock-condition.js +70 -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 +20 -0
  20. package/dist/conditions/not-armed-condition.js.map +1 -0
  21. package/dist/constants/default-constant.d.ts +26 -0
  22. package/dist/constants/default-constant.js +28 -0
  23. package/dist/constants/default-constant.js.map +1 -0
  24. package/dist/constants/homekit-constant.d.ts +4 -0
  25. package/dist/constants/homekit-constant.js +5 -0
  26. package/dist/constants/homekit-constant.js.map +1 -0
  27. package/dist/constants/switch-uuid-constant.d.ts +23 -0
  28. package/dist/constants/switch-uuid-constant.js +24 -0
  29. package/dist/constants/switch-uuid-constant.js.map +1 -0
  30. package/dist/handlers/sensor-handler.d.ts +17 -0
  31. package/dist/handlers/sensor-handler.js +53 -0
  32. package/dist/handlers/sensor-handler.js.map +1 -0
  33. package/dist/handlers/state-handler.d.ts +41 -0
  34. package/dist/handlers/state-handler.js +213 -0
  35. package/dist/handlers/state-handler.js.map +1 -0
  36. package/dist/handlers/switch-handler.d.ts +27 -0
  37. package/dist/handlers/switch-handler.js +139 -0
  38. package/dist/handlers/switch-handler.js.map +1 -0
  39. package/dist/handlers/trip-handler.d.ts +45 -0
  40. package/dist/handlers/trip-handler.js +179 -0
  41. package/dist/handlers/trip-handler.js.map +1 -0
  42. package/dist/index.d.ts +3 -0
  43. package/dist/index.js +7 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/interfaces/condition-context-interface.d.ts +13 -0
  46. package/dist/interfaces/condition-context-interface.js +2 -0
  47. package/dist/interfaces/condition-context-interface.js.map +1 -0
  48. package/dist/interfaces/hap-types-interface.d.ts +9 -0
  49. package/dist/interfaces/hap-types-interface.js +2 -0
  50. package/dist/interfaces/hap-types-interface.js.map +1 -0
  51. package/dist/interfaces/options-interface.d.ts +93 -0
  52. package/dist/interfaces/options-interface.js +2 -0
  53. package/dist/interfaces/options-interface.js.map +1 -0
  54. package/dist/interfaces/persisted-state-interface.d.ts +6 -0
  55. package/dist/interfaces/persisted-state-interface.js +2 -0
  56. package/dist/interfaces/persisted-state-interface.js.map +1 -0
  57. package/dist/interfaces/service-registry-interface.d.ts +26 -0
  58. package/dist/interfaces/service-registry-interface.js +2 -0
  59. package/dist/interfaces/service-registry-interface.js.map +1 -0
  60. package/dist/interfaces/system-state-interface.d.ts +21 -0
  61. package/dist/interfaces/system-state-interface.js +2 -0
  62. package/dist/interfaces/system-state-interface.js.map +1 -0
  63. package/dist/security-system.d.ts +26 -0
  64. package/dist/security-system.js +377 -0
  65. package/dist/security-system.js.map +1 -0
  66. package/dist/services/audio-service.d.ts +21 -0
  67. package/dist/services/audio-service.js +113 -0
  68. package/dist/services/audio-service.js.map +1 -0
  69. package/dist/services/command-service.d.ts +16 -0
  70. package/dist/services/command-service.js +65 -0
  71. package/dist/services/command-service.js.map +1 -0
  72. package/dist/services/event-bus-service.d.ts +12 -0
  73. package/dist/services/event-bus-service.js +17 -0
  74. package/dist/services/event-bus-service.js.map +1 -0
  75. package/dist/services/server-service.d.ts +25 -0
  76. package/dist/services/server-service.js +140 -0
  77. package/dist/services/server-service.js.map +1 -0
  78. package/dist/services/storage-service.d.ts +14 -0
  79. package/dist/services/storage-service.js +70 -0
  80. package/dist/services/storage-service.js.map +1 -0
  81. package/dist/services/webhook-service.d.ts +16 -0
  82. package/dist/services/webhook-service.js +73 -0
  83. package/dist/services/webhook-service.js.map +1 -0
  84. package/dist/tests/conditions.test.d.ts +1 -0
  85. package/dist/tests/conditions.test.js +151 -0
  86. package/dist/tests/conditions.test.js.map +1 -0
  87. package/dist/tests/state-handler.test.d.ts +1 -0
  88. package/dist/tests/state-handler.test.js +171 -0
  89. package/dist/tests/state-handler.test.js.map +1 -0
  90. package/dist/tests/trip-handler.test.d.ts +1 -0
  91. package/dist/tests/trip-handler.test.js +160 -0
  92. package/dist/tests/trip-handler.test.js.map +1 -0
  93. package/dist/types/event-payload-map-type.d.ts +9 -0
  94. package/dist/types/event-payload-map-type.js +2 -0
  95. package/dist/types/event-payload-map-type.js.map +1 -0
  96. package/dist/types/event-type.d.ts +24 -0
  97. package/dist/types/event-type.js +9 -0
  98. package/dist/types/event-type.js.map +1 -0
  99. package/dist/types/mode-type.d.ts +4 -0
  100. package/dist/types/mode-type.js +2 -0
  101. package/dist/types/mode-type.js.map +1 -0
  102. package/dist/types/origin-type.d.ts +9 -0
  103. package/dist/types/origin-type.js +11 -0
  104. package/dist/types/origin-type.js.map +1 -0
  105. package/dist/types/security-state-type.d.ts +11 -0
  106. package/dist/types/security-state-type.js +13 -0
  107. package/dist/types/security-state-type.js.map +1 -0
  108. package/dist/utils/log-util.d.ts +4 -0
  109. package/dist/utils/log-util.js +47 -0
  110. package/dist/utils/log-util.js.map +1 -0
  111. package/dist/utils/options-util.d.ts +6 -0
  112. package/dist/utils/options-util.js +171 -0
  113. package/dist/utils/options-util.js.map +1 -0
  114. package/dist/utils/state-util.d.ts +8 -0
  115. package/dist/utils/state-util.js +29 -0
  116. package/dist/utils/state-util.js.map +1 -0
  117. package/eslint.config.js +35 -0
  118. package/package.json +40 -34
  119. package/src/@types/homebridge-lib.d.ts +14 -0
  120. package/src/conditions/already-triggered-condition.ts +18 -0
  121. package/src/conditions/arming-in-progress-condition.ts +17 -0
  122. package/src/conditions/arming-lock-condition.ts +48 -0
  123. package/src/conditions/condition.ts +17 -0
  124. package/src/conditions/double-knock-condition.ts +82 -0
  125. package/src/conditions/not-armed-condition.ts +24 -0
  126. package/src/constants/default-constant.ts +28 -0
  127. package/src/constants/homekit-constant.ts +5 -0
  128. package/src/constants/switch-uuid-constant.ts +23 -0
  129. package/src/handlers/sensor-handler.ts +81 -0
  130. package/src/handlers/state-handler.ts +268 -0
  131. package/src/handlers/switch-handler.ts +166 -0
  132. package/src/handlers/trip-handler.ts +219 -0
  133. package/src/index.ts +9 -0
  134. package/src/interfaces/condition-context-interface.ts +14 -0
  135. package/src/interfaces/hap-types-interface.ts +11 -0
  136. package/src/interfaces/options-interface.ts +120 -0
  137. package/src/interfaces/persisted-state-interface.ts +7 -0
  138. package/src/interfaces/service-registry-interface.ts +37 -0
  139. package/src/interfaces/system-state-interface.ts +25 -0
  140. package/src/security-system.ts +433 -0
  141. package/src/services/audio-service.ts +133 -0
  142. package/src/services/command-service.ts +76 -0
  143. package/src/services/event-bus-service.ts +21 -0
  144. package/src/services/server-service.ts +176 -0
  145. package/src/services/storage-service.ts +80 -0
  146. package/src/services/webhook-service.ts +83 -0
  147. package/src/tests/conditions.test.ts +180 -0
  148. package/src/tests/state-handler.test.ts +200 -0
  149. package/src/tests/trip-handler.test.ts +182 -0
  150. package/src/types/event-payload-map-type.ts +15 -0
  151. package/src/types/event-type.ts +29 -0
  152. package/src/types/mode-type.ts +4 -0
  153. package/src/types/origin-type.ts +9 -0
  154. package/src/types/security-state-type.ts +11 -0
  155. package/src/utils/log-util.ts +54 -0
  156. package/src/utils/options-util.ts +202 -0
  157. package/src/utils/state-util.ts +32 -0
  158. package/tsconfig.json +25 -0
  159. package/vitest.config.ts +8 -0
  160. package/.eslintrc.js +0 -16
  161. package/.vscode/settings.json +0 -12
  162. package/src/index.js +0 -2525
  163. package/src/utils/options.js +0 -409
@@ -0,0 +1,219 @@
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 { HK_NOT_ALLOWED_IN_CURRENT_STATE } from '../constants/homekit-constant.js';
6
+ import type { ServiceRegistry } from '../interfaces/service-registry-interface.js';
7
+ import type { SystemState } from '../interfaces/system-state-interface.js';
8
+ import type { SecuritySystemOptions } from '../interfaces/options-interface.js';
9
+ import type { EventBusService } from '../services/event-bus-service.js';
10
+ import { EventType } from '../types/event-type.js';
11
+ import type { AudioService } from '../services/audio-service.js';
12
+ import type { SensorHandler } from './sensor-handler.js';
13
+ import type { StateHandler } from './state-handler.js';
14
+ import { NotArmedCondition } from '../conditions/not-armed-condition.js';
15
+ import { ArmingInProgressCondition } from '../conditions/arming-in-progress-condition.js';
16
+ import { AlreadyTriggeredCondition } from '../conditions/already-triggered-condition.js';
17
+ import { DoubleKnockCondition } from '../conditions/double-knock-condition.js';
18
+ import type { ConditionContext } from '../interfaces/condition-context-interface.js';
19
+
20
+ /** Handles the trip switch and trigger-delay logic, including all blocking conditions. */
21
+ export class TripHandler {
22
+ private stateHandler!: StateHandler;
23
+
24
+ private readonly notArmed = new NotArmedCondition();
25
+ private readonly armingInProgress = new ArmingInProgressCondition();
26
+ private readonly alreadyTriggered = new AlreadyTriggeredCondition();
27
+ private readonly doubleKnock: DoubleKnockCondition;
28
+
29
+ constructor(
30
+ private readonly services: ServiceRegistry,
31
+ private readonly state: SystemState,
32
+ private readonly options: SecuritySystemOptions,
33
+ private readonly Characteristic: CharacteristicConstructor,
34
+ private readonly log: Logging,
35
+ private readonly bus: EventBusService,
36
+ private readonly audio: AudioService,
37
+ private readonly sensorHandler: SensorHandler,
38
+ ) {
39
+ this.doubleKnock = new DoubleKnockCondition((seconds, onExpire) => {
40
+ this.log.warn('Trip Switch (Knock)');
41
+ this.state.doubleKnockTimeout = setTimeout(() => {
42
+ onExpire();
43
+ this.log.info('Trip Switch (Reset)');
44
+ }, seconds * 1000);
45
+ });
46
+ }
47
+
48
+ setStateHandler(handler: StateHandler): void {
49
+ this.stateHandler = handler;
50
+ }
51
+
52
+ /**
53
+ * Core trip-switch logic shared by all trip/trigger paths.
54
+ * Returns `true` on success, `false` if blocked.
55
+ */
56
+ updateTripSwitch(value: boolean, origin: OriginType, stateChanged: boolean): boolean {
57
+ const ctx: ConditionContext = {
58
+ state: this.state,
59
+ services: this.services,
60
+ options: this.options,
61
+ value,
62
+ origin,
63
+ };
64
+
65
+ if (value) {
66
+ // Check blocking conditions in order.
67
+ if (this.notArmed.evaluate(ctx)) {
68
+ this.log.warn('Trip Switch (Not armed)');
69
+ return false;
70
+ }
71
+
72
+ if (this.armingInProgress.evaluate(ctx)) {
73
+ this.log.warn('Trip Switch (Still arming)');
74
+ return false;
75
+ }
76
+
77
+ if (this.doubleKnock.evaluate(ctx)) {
78
+ return false;
79
+ }
80
+
81
+ // Clear any lingering double-knock timeout once we've passed it.
82
+ if (this.state.doubleKnockTimeout !== null) {
83
+ clearTimeout(this.state.doubleKnockTimeout);
84
+ this.state.doubleKnockTimeout = null;
85
+ }
86
+
87
+ if (this.alreadyTriggered.evaluate(ctx)) {
88
+ this.log.warn('Security System (Already triggered)');
89
+ return false;
90
+ }
91
+
92
+ if (this.state.triggerTimeout !== null) {
93
+ this.log.warn('Security System (Already tripped)');
94
+ return false;
95
+ }
96
+
97
+ this.activateTrip(origin);
98
+ } else {
99
+ this.cancelTrip(origin, stateChanged);
100
+ }
101
+
102
+ // Sync trip switch characteristic when origin is not a direct switch press.
103
+ if (origin === OriginType.INTERNAL || origin === OriginType.EXTERNAL) {
104
+ this.services.tripSwitchService.updateCharacteristic(this.Characteristic.On, value);
105
+ }
106
+
107
+ return true;
108
+ }
109
+
110
+ /**
111
+ * Trip a mode-specific switch. Only triggers if the system is currently in
112
+ * the required mode (or the alarm is triggered and target matches).
113
+ */
114
+ triggerIfModeSet(requiredState: SecurityState, value: boolean): boolean {
115
+ const isTriggered = this.state.currentState === SecurityState.TRIGGERED;
116
+
117
+ if (value) {
118
+ const modeMatches = this.state.currentState === requiredState
119
+ || (isTriggered && this.state.targetState === requiredState);
120
+
121
+ if (!modeMatches) {
122
+ this.log.debug('Security System (Trip mode not set)');
123
+ return false;
124
+ }
125
+ }
126
+
127
+ return this.updateTripSwitch(value, OriginType.REGULAR_SWITCH, false);
128
+ }
129
+
130
+ resetTripSwitches(): void {
131
+ const switches: Array<[keyof ServiceRegistry, string]> = [
132
+ ['tripHomeSwitchService', 'Trip Home'],
133
+ ['tripAwaySwitchService', 'Trip Away'],
134
+ ['tripNightSwitchService', 'Trip Night'],
135
+ ['tripOverrideSwitchService', 'Trip Override'],
136
+ ];
137
+
138
+ for (const [key, label] of switches) {
139
+ const char = this.services[key].getCharacteristic(this.Characteristic.On);
140
+ if (char.value) {
141
+ char.updateValue(false);
142
+ this.log.debug(`${label} Switch (Off)`);
143
+ }
144
+ }
145
+ }
146
+
147
+ private activateTrip(origin: OriginType): void {
148
+ this.log.info('Security System (Tripped)');
149
+
150
+ if (this.options.trippedMotionSensor) {
151
+ this.sensorHandler.pulseTrippedMotionSensor();
152
+ this.state.trippedMotionSensorInterval = setInterval(
153
+ () => this.sensorHandler.pulseTrippedMotionSensor(),
154
+ this.options.trippedMotionSensorSeconds * 1000,
155
+ );
156
+ }
157
+
158
+ const triggerSeconds = this.resolveTriggerSeconds();
159
+ this.log.debug(`Trigger delay (${triggerSeconds}s)`);
160
+
161
+ this.state.triggerTimeout = setTimeout(() => {
162
+ this.state.triggerTimeout = null;
163
+ this.stateHandler.setCurrentState(SecurityState.TRIGGERED, origin);
164
+ }, triggerSeconds * 1000);
165
+
166
+ if (triggerSeconds > 0) {
167
+ this.bus.emit(EventType.WARNING, { origin, triggerSeconds });
168
+ }
169
+ }
170
+
171
+ private cancelTrip(origin: OriginType, stateChanged: boolean): void {
172
+ this.log.info('Security System (Cancelled)');
173
+ this.audio.stop();
174
+
175
+ if (this.state.currentState === SecurityState.TRIGGERED) {
176
+ if (!stateChanged) {
177
+ this.stateHandler.updateTargetState(SecurityState.OFF, OriginType.INTERNAL, 0);
178
+ }
179
+ } else {
180
+ this.stateHandler.resetTimers();
181
+ }
182
+
183
+ if (this.options.trippedMotionSensor) {
184
+ this.sensorHandler.resetTrippedMotionSensor();
185
+ }
186
+
187
+ this.state.isKnocked = false;
188
+ }
189
+
190
+ private resolveTriggerSeconds(): number {
191
+ const seconds = this.options.triggerSeconds;
192
+ const cur = this.state.currentState;
193
+
194
+ if (cur === SecurityState.HOME && this.options.homeTriggerSeconds !== null) {
195
+ return this.options.homeTriggerSeconds;
196
+ }
197
+
198
+ if (cur === SecurityState.AWAY) {
199
+ const extChar = this.services.modeAwayExtendedSwitchService
200
+ .getCharacteristic(this.Characteristic.On);
201
+
202
+ if (this.options.modeAwayExtendedSwitchTriggerSeconds !== null && extChar.value) {
203
+ return this.options.modeAwayExtendedSwitchTriggerSeconds;
204
+ }
205
+
206
+ if (this.options.awayTriggerSeconds !== null) {
207
+ return this.options.awayTriggerSeconds;
208
+ }
209
+ }
210
+
211
+ if (cur === SecurityState.NIGHT && this.options.nightTriggerSeconds !== null) {
212
+ return this.options.nightTriggerSeconds;
213
+ }
214
+
215
+ return seconds;
216
+ }
217
+ }
218
+
219
+ export { HK_NOT_ALLOWED_IN_CURRENT_STATE };
package/src/index.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { API } from 'homebridge';
2
+ import { SecuritySystem } from './security-system.js';
3
+
4
+ const PLUGIN_NAME = 'homebridge-securitysystem';
5
+ const ACCESSORY_NAME = 'security-system';
6
+
7
+ export default (api: API): void => {
8
+ api.registerAccessory(PLUGIN_NAME, ACCESSORY_NAME, SecuritySystem);
9
+ };
@@ -0,0 +1,14 @@
1
+ import type { OriginType } from '../types/origin-type.js';
2
+ import type { SecuritySystemOptions } from './options-interface.js';
3
+ import type { ServiceRegistry } from './service-registry-interface.js';
4
+ import type { SystemState } from './system-state-interface.js';
5
+
6
+ /** Context passed to Condition.evaluate() to determine if an action is blocked. */
7
+ export interface ConditionContext {
8
+ state: SystemState;
9
+ services: ServiceRegistry;
10
+ options: SecuritySystemOptions;
11
+ /** The value being set (true = activating, false = deactivating). */
12
+ value: boolean;
13
+ origin: OriginType;
14
+ }
@@ -0,0 +1,11 @@
1
+ import type { API } from 'homebridge';
2
+
3
+ /**
4
+ * Runtime type of `api.hap.Characteristic` (the class with all static characteristic
5
+ * definitions). Using `API['hap']['Characteristic']` avoids the self-referential
6
+ * `typeof Characteristic` issue that arises with `import type { Characteristic }`.
7
+ */
8
+ export type CharacteristicConstructor = API['hap']['Characteristic'];
9
+
10
+ /** Runtime type of `api.hap.Service`. */
11
+ export type ServiceConstructor = API['hap']['Service'];
@@ -0,0 +1,120 @@
1
+ /** Key-value pair for extra ffplay environment variables. */
2
+ export interface AudioVariable {
3
+ key: string;
4
+ value: string;
5
+ }
6
+
7
+ /** Fully-parsed, strongly-typed configuration options for the security system. */
8
+ export interface SecuritySystemOptions {
9
+ name: string;
10
+ serialNumber: string;
11
+ defaultMode: string;
12
+ armSeconds: number;
13
+ triggerSeconds: number;
14
+ resetMinutes: number;
15
+ saveState: boolean;
16
+ proxyMode: boolean;
17
+ testMode: boolean;
18
+
19
+ // Switch display names
20
+ tripSwitchName: string;
21
+ tripHomeSwitchName: string;
22
+ tripAwaySwitchName: string;
23
+ tripNightSwitchName: string;
24
+ tripOverrideSwitchName: string;
25
+ modeHomeSwitchName: string;
26
+ modeAwaySwitchName: string;
27
+ modeNightSwitchName: string;
28
+ modeOffSwitchName: string;
29
+ modeAwayExtendedSwitchName: string;
30
+ modePauseSwitchName: string;
31
+ audioSwitchName: string;
32
+
33
+ // Behaviour toggles
34
+ logDirectory: string | null;
35
+ overrideOff: boolean;
36
+ resetOffFlow: boolean;
37
+ disabledModes: string[];
38
+
39
+ // Per-mode arm delays (null = use global armSeconds)
40
+ homeArmSeconds: number | null;
41
+ awayArmSeconds: number | null;
42
+ nightArmSeconds: number | null;
43
+
44
+ // Per-mode trigger delays (null = use global triggerSeconds)
45
+ homeTriggerSeconds: number | null;
46
+ awayTriggerSeconds: number | null;
47
+ nightTriggerSeconds: number | null;
48
+
49
+ // Trip switches
50
+ tripSwitch: boolean;
51
+ tripOverrideSwitch: boolean;
52
+ tripModeSwitches: boolean;
53
+
54
+ // Arming lock
55
+ armingLockSwitch: boolean;
56
+ armingLockSwitches: boolean;
57
+
58
+ // Motion sensors
59
+ armingMotionSensor: boolean;
60
+ trippedMotionSensor: boolean;
61
+ trippedMotionSensorSeconds: number;
62
+ triggeredMotionSensor: boolean;
63
+ triggeredMotionSensorSeconds: number;
64
+ resetSensor: boolean;
65
+
66
+ // Mode switches
67
+ modeSwitches: boolean;
68
+ modeOffSwitch: boolean;
69
+ modePauseSwitch: boolean;
70
+ pauseMinutes: number;
71
+ modeAwayExtendedSwitch: boolean;
72
+ modeAwayExtendedSwitchTriggerSeconds: number | null;
73
+
74
+ // Double-knock
75
+ doubleKnock: boolean;
76
+ doubleKnockSeconds: number;
77
+ doubleKnockModes: string[];
78
+ homeDoubleKnockSeconds: number | null;
79
+ awayDoubleKnockSeconds: number | null;
80
+ nightDoubleKnockSeconds: number | null;
81
+
82
+ // Audio
83
+ audio: boolean;
84
+ audioPath: string | null;
85
+ audioLanguage: string;
86
+ audioVolume: number | null;
87
+ audioArmingLooped: boolean;
88
+ audioAlertLooped: boolean;
89
+ audioExtraVariables: AudioVariable[];
90
+ audioSwitch: boolean;
91
+
92
+ // Server
93
+ serverPort: number | null;
94
+ serverCode: number | null;
95
+
96
+ // Shell commands
97
+ commandTargetHome: string | null;
98
+ commandTargetAway: string | null;
99
+ commandTargetNight: string | null;
100
+ commandTargetOff: string | null;
101
+ commandCurrentHome: string | null;
102
+ commandCurrentAway: string | null;
103
+ commandCurrentNight: string | null;
104
+ commandCurrentOff: string | null;
105
+ commandCurrentWarning: string | null;
106
+ commandCurrentTriggered: string | null;
107
+
108
+ // Webhooks
109
+ webhookUrl: string | null;
110
+ webhookTargetHome: string | null;
111
+ webhookTargetAway: string | null;
112
+ webhookTargetNight: string | null;
113
+ webhookTargetOff: string | null;
114
+ webhookCurrentHome: string | null;
115
+ webhookCurrentAway: string | null;
116
+ webhookCurrentNight: string | null;
117
+ webhookCurrentOff: string | null;
118
+ webhookCurrentWarning: string | null;
119
+ webhookCurrentTriggered: string | null;
120
+ }
@@ -0,0 +1,7 @@
1
+ import type { SecurityState } from '../types/security-state-type.js';
2
+
3
+ /** Subset of SystemState that is persisted across Homebridge restarts. */
4
+ export interface PersistedState {
5
+ currentState: SecurityState;
6
+ targetState: SecurityState;
7
+ }
@@ -0,0 +1,37 @@
1
+ import type { Service } from 'homebridge';
2
+
3
+ /** All HomeKit services exposed by the security system accessory. */
4
+ export interface ServiceRegistry {
5
+ mainService: Service;
6
+ accessoryInfoService: Service;
7
+
8
+ // Trip switches
9
+ tripSwitchService: Service;
10
+ tripHomeSwitchService: Service;
11
+ tripAwaySwitchService: Service;
12
+ tripNightSwitchService: Service;
13
+ tripOverrideSwitchService: Service;
14
+
15
+ // Arming lock switches
16
+ armingLockSwitchService: Service;
17
+ armingLockHomeSwitchService: Service;
18
+ armingLockAwaySwitchService: Service;
19
+ armingLockNightSwitchService: Service;
20
+
21
+ // Mode switches
22
+ modeHomeSwitchService: Service;
23
+ modeAwaySwitchService: Service;
24
+ modeNightSwitchService: Service;
25
+ modeOffSwitchService: Service;
26
+ modeAwayExtendedSwitchService: Service;
27
+ modePauseSwitchService: Service;
28
+
29
+ // Audio switch
30
+ audioSwitchService: Service;
31
+
32
+ // Motion sensors
33
+ armingMotionSensorService: Service;
34
+ trippedMotionSensorService: Service;
35
+ triggeredMotionSensorService: Service;
36
+ triggeredResetMotionSensorService: Service;
37
+ }
@@ -0,0 +1,25 @@
1
+ import type { ChildProcess } from 'child_process';
2
+ import type { SecurityState } from '../types/security-state-type.js';
3
+
4
+ /** Mutable runtime state shared across all handlers. */
5
+ export interface SystemState {
6
+ currentState: SecurityState;
7
+ targetState: SecurityState;
8
+ defaultState: SecurityState;
9
+ availableTargetStates: SecurityState[];
10
+
11
+ isArming: boolean;
12
+ isKnocked: boolean;
13
+ invalidCodeCount: number;
14
+ pausedCurrentState: SecurityState | null;
15
+ audioProcess: ChildProcess | null;
16
+
17
+ // Active timers (null = not running)
18
+ armTimeout: ReturnType<typeof setTimeout> | null;
19
+ pauseTimeout: ReturnType<typeof setTimeout> | null;
20
+ triggerTimeout: ReturnType<typeof setTimeout> | null;
21
+ doubleKnockTimeout: ReturnType<typeof setTimeout> | null;
22
+ resetTimeout: ReturnType<typeof setTimeout> | null;
23
+ trippedMotionSensorInterval: ReturnType<typeof setInterval> | null;
24
+ triggeredMotionSensorInterval: ReturnType<typeof setInterval> | null;
25
+ }