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,171 @@
1
+ import { DEFAULTS } from '../constants/default-constant.js';
2
+ function isSet(value) {
3
+ if (value === undefined || value === null) {
4
+ return false;
5
+ }
6
+ if (typeof value === 'string' && (value.trim() === '' || value === 'null')) {
7
+ return false;
8
+ }
9
+ return true;
10
+ }
11
+ function str(raw, key) {
12
+ return isSet(raw[key]) ? String(raw[key]) : null;
13
+ }
14
+ function num(raw, key) {
15
+ const v = raw[key];
16
+ if (!isSet(v)) {
17
+ return null;
18
+ }
19
+ const n = Number(v);
20
+ return isNaN(n) ? null : n;
21
+ }
22
+ function bool(raw, key, def) {
23
+ return isSet(raw[key]) ? Boolean(raw[key]) : def;
24
+ }
25
+ function strArr(raw, key) {
26
+ return Array.isArray(raw[key]) ? raw[key].map(String) : [];
27
+ }
28
+ /** Migrate deprecated config keys to their current equivalents. */
29
+ function applyDeprecations(log, raw) {
30
+ const renames = {
31
+ siren_switch: 'trip_switch',
32
+ siren_override_switch: 'trip_override_switch',
33
+ siren_mode_switches: 'trip_mode_switches',
34
+ siren_sensor: 'triggered_sensor',
35
+ siren_sensor_seconds: 'triggered_sensor_seconds',
36
+ };
37
+ for (const [oldKey, newKey] of Object.entries(renames)) {
38
+ if (isSet(raw[oldKey]) && !isSet(raw[newKey])) {
39
+ log.warn(`Config: '${oldKey}' is deprecated, use '${newKey}' instead.`);
40
+ raw[newKey] = raw[oldKey];
41
+ }
42
+ }
43
+ }
44
+ function validate(log, opts) {
45
+ if (opts.resetMinutes === 0) {
46
+ log.error('\'reset_minutes\' must be at least 1. Defaulting to 1.');
47
+ opts.resetMinutes = 1;
48
+ }
49
+ if (opts.serverPort !== null && (opts.serverPort < 0 || opts.serverPort > 65535)) {
50
+ log.error('\'server_port\' must be between 0 and 65535.');
51
+ }
52
+ }
53
+ function normalize(opts) {
54
+ opts.defaultMode = opts.defaultMode.toLowerCase();
55
+ if (opts.testMode) {
56
+ opts.webhookCurrentTriggered = null;
57
+ opts.commandCurrentTriggered = null;
58
+ }
59
+ }
60
+ /** Parse, validate and normalise raw homebridge config into a typed options object. */
61
+ export function parseOptions(log, raw) {
62
+ log.info('Config', JSON.stringify(raw));
63
+ applyDeprecations(log, raw);
64
+ const opts = {
65
+ name: str(raw, 'name') ?? DEFAULTS.NAME,
66
+ serialNumber: str(raw, 'serial_number') ?? DEFAULTS.SERIAL_NUMBER,
67
+ defaultMode: str(raw, 'default_mode') ?? DEFAULTS.DEFAULT_MODE,
68
+ armSeconds: num(raw, 'arm_seconds') ?? DEFAULTS.ARM_SECONDS,
69
+ triggerSeconds: num(raw, 'trigger_seconds') ?? DEFAULTS.TRIGGER_SECONDS,
70
+ resetMinutes: num(raw, 'reset_minutes') ?? DEFAULTS.RESET_MINUTES,
71
+ saveState: bool(raw, 'save_state', false),
72
+ proxyMode: bool(raw, 'proxy_mode', false),
73
+ testMode: bool(raw, 'test_mode', false),
74
+ // Names
75
+ tripSwitchName: str(raw, 'trip_switch_name') ?? DEFAULTS.TRIP_SWITCH_NAME,
76
+ tripHomeSwitchName: str(raw, 'trip_home_switch_name') ?? DEFAULTS.TRIP_HOME_SWITCH_NAME,
77
+ tripAwaySwitchName: str(raw, 'trip_away_switch_name') ?? DEFAULTS.TRIP_AWAY_SWITCH_NAME,
78
+ tripNightSwitchName: str(raw, 'trip_night_switch_name') ?? DEFAULTS.TRIP_NIGHT_SWITCH_NAME,
79
+ tripOverrideSwitchName: str(raw, 'trip_override_switch_name') ?? DEFAULTS.TRIP_OVERRIDE_SWITCH_NAME,
80
+ modeHomeSwitchName: str(raw, 'mode_home_switch_name') ?? DEFAULTS.MODE_HOME_SWITCH_NAME,
81
+ modeAwaySwitchName: str(raw, 'mode_away_switch_name') ?? DEFAULTS.MODE_AWAY_SWITCH_NAME,
82
+ modeNightSwitchName: str(raw, 'mode_night_switch_name') ?? DEFAULTS.MODE_NIGHT_SWITCH_NAME,
83
+ modeOffSwitchName: str(raw, 'mode_off_switch_name') ?? DEFAULTS.MODE_OFF_SWITCH_NAME,
84
+ modeAwayExtendedSwitchName: str(raw, 'mode_away_extended_switch_name') ?? DEFAULTS.MODE_AWAY_EXTENDED_SWITCH_NAME,
85
+ modePauseSwitchName: str(raw, 'mode_pause_switch_name') ?? DEFAULTS.MODE_PAUSE_SWITCH_NAME,
86
+ audioSwitchName: str(raw, 'audio_switch_name') ?? DEFAULTS.AUDIO_SWITCH_NAME,
87
+ // Behaviour
88
+ logDirectory: str(raw, 'log_directory'),
89
+ overrideOff: bool(raw, 'override_off', false),
90
+ resetOffFlow: bool(raw, 'reset_off_flow', false),
91
+ disabledModes: strArr(raw, 'disabled_modes'),
92
+ // Arm delays
93
+ homeArmSeconds: num(raw, 'home_arm_seconds'),
94
+ awayArmSeconds: num(raw, 'away_arm_seconds'),
95
+ nightArmSeconds: num(raw, 'night_arm_seconds'),
96
+ // Trigger delays
97
+ homeTriggerSeconds: num(raw, 'home_trigger_seconds'),
98
+ awayTriggerSeconds: num(raw, 'away_trigger_seconds'),
99
+ nightTriggerSeconds: num(raw, 'night_trigger_seconds'),
100
+ // Trip switches
101
+ tripSwitch: bool(raw, 'trip_switch', false),
102
+ tripOverrideSwitch: bool(raw, 'trip_override_switch', false),
103
+ tripModeSwitches: bool(raw, 'trip_mode_switches', true),
104
+ // Arming lock
105
+ armingLockSwitch: bool(raw, 'arming_lock_switch', false),
106
+ armingLockSwitches: bool(raw, 'arming_lock_switches', false),
107
+ // Sensors
108
+ armingMotionSensor: bool(raw, 'arming_sensor', false),
109
+ trippedMotionSensor: bool(raw, 'tripped_sensor', false),
110
+ trippedMotionSensorSeconds: num(raw, 'tripped_sensor_seconds') ?? DEFAULTS.TRIPPED_SENSOR_SECONDS,
111
+ triggeredMotionSensor: bool(raw, 'triggered_sensor', false),
112
+ triggeredMotionSensorSeconds: num(raw, 'triggered_sensor_seconds') ?? DEFAULTS.TRIGGERED_SENSOR_SECONDS,
113
+ resetSensor: bool(raw, 'reset_sensor', false),
114
+ // Mode switches
115
+ modeSwitches: bool(raw, 'mode_switches', false),
116
+ modeOffSwitch: bool(raw, 'mode_off_switch', false),
117
+ modePauseSwitch: bool(raw, 'mode_pause_switch', false),
118
+ pauseMinutes: num(raw, 'pause_minutes') ?? DEFAULTS.PAUSE_MINUTES,
119
+ modeAwayExtendedSwitch: bool(raw, 'mode_away_extended_switch', false),
120
+ modeAwayExtendedSwitchTriggerSeconds: num(raw, 'mode_away_extended_switch_trigger_seconds'),
121
+ // Double-knock
122
+ doubleKnock: bool(raw, 'double_knock', false),
123
+ doubleKnockSeconds: num(raw, 'double_knock_seconds') ?? DEFAULTS.DOUBLE_KNOCK_SECONDS,
124
+ doubleKnockModes: strArr(raw, 'double_knock_modes'),
125
+ homeDoubleKnockSeconds: num(raw, 'home_double_knock_seconds'),
126
+ awayDoubleKnockSeconds: num(raw, 'away_double_knock_seconds'),
127
+ nightDoubleKnockSeconds: num(raw, 'night_double_knock_seconds'),
128
+ // Audio
129
+ audio: bool(raw, 'audio', false),
130
+ audioPath: str(raw, 'audio_path'),
131
+ audioLanguage: str(raw, 'audio_language') ?? DEFAULTS.AUDIO_LANGUAGE,
132
+ audioVolume: num(raw, 'audio_volume'),
133
+ audioArmingLooped: bool(raw, 'audio_arming_looped', false),
134
+ audioAlertLooped: bool(raw, 'audio_alert_looped', false),
135
+ audioExtraVariables: Array.isArray(raw.audio_extra_variables)
136
+ ? raw.audio_extra_variables
137
+ : [],
138
+ audioSwitch: bool(raw, 'audio_switch', false),
139
+ // Server
140
+ serverPort: num(raw, 'server_port'),
141
+ serverCode: num(raw, 'server_code'),
142
+ // Commands
143
+ commandTargetHome: str(raw, 'command_target_home'),
144
+ commandTargetAway: str(raw, 'command_target_away'),
145
+ commandTargetNight: str(raw, 'command_target_night'),
146
+ commandTargetOff: str(raw, 'command_target_off'),
147
+ commandCurrentHome: str(raw, 'command_current_home'),
148
+ commandCurrentAway: str(raw, 'command_current_away'),
149
+ commandCurrentNight: str(raw, 'command_current_night'),
150
+ commandCurrentOff: str(raw, 'command_current_off'),
151
+ commandCurrentWarning: str(raw, 'command_current_warning'),
152
+ commandCurrentTriggered: str(raw, 'command_current_triggered'),
153
+ // Webhooks
154
+ webhookUrl: str(raw, 'webhook_url'),
155
+ webhookTargetHome: str(raw, 'webhook_target_home'),
156
+ webhookTargetAway: str(raw, 'webhook_target_away'),
157
+ webhookTargetNight: str(raw, 'webhook_target_night'),
158
+ webhookTargetOff: str(raw, 'webhook_target_off'),
159
+ webhookCurrentHome: str(raw, 'webhook_current_home'),
160
+ webhookCurrentAway: str(raw, 'webhook_current_away'),
161
+ webhookCurrentNight: str(raw, 'webhook_current_night'),
162
+ webhookCurrentOff: str(raw, 'webhook_current_off'),
163
+ webhookCurrentWarning: str(raw, 'webhook_current_warning'),
164
+ webhookCurrentTriggered: str(raw, 'webhook_current_triggered'),
165
+ };
166
+ validate(log, opts);
167
+ normalize(opts);
168
+ log.info('Options', JSON.stringify(opts));
169
+ return opts;
170
+ }
171
+ //# sourceMappingURL=options-util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options-util.js","sourceRoot":"","sources":["../../src/utils/options-util.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { SecurityState } from '../types/security-state-type.js';
2
+ import type { Mode } from '../types/mode-type.js';
3
+ /** Converts a SecurityState value to its human-readable mode string. */
4
+ export declare function stateToMode(state: SecurityState | string): Mode;
5
+ /** Converts a mode string to its corresponding SecurityState value (-1 if unknown). */
6
+ export declare function modeToState(mode: string): SecurityState;
7
+ /** Capitalises the first letter of a string. */
8
+ export declare function capitalise(s: string): string;
@@ -0,0 +1,29 @@
1
+ import { SecurityState } from '../types/security-state-type.js';
2
+ /** Converts a SecurityState value to its human-readable mode string. */
3
+ export function stateToMode(state) {
4
+ switch (state) {
5
+ case SecurityState.TRIGGERED: return 'triggered';
6
+ case SecurityState.HOME: return 'home';
7
+ case SecurityState.AWAY: return 'away';
8
+ case SecurityState.NIGHT: return 'night';
9
+ case SecurityState.OFF: return 'off';
10
+ case 'lock': return 'lock';
11
+ case 'warning': return 'warning';
12
+ default: return 'unknown';
13
+ }
14
+ }
15
+ /** Converts a mode string to its corresponding SecurityState value (-1 if unknown). */
16
+ export function modeToState(mode) {
17
+ switch (mode) {
18
+ case 'home': return SecurityState.HOME;
19
+ case 'away': return SecurityState.AWAY;
20
+ case 'night': return SecurityState.NIGHT;
21
+ case 'off': return SecurityState.OFF;
22
+ default: return -1;
23
+ }
24
+ }
25
+ /** Capitalises the first letter of a string. */
26
+ export function capitalise(s) {
27
+ return s.charAt(0).toUpperCase() + s.slice(1);
28
+ }
29
+ //# sourceMappingURL=state-util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-util.js","sourceRoot":"","sources":["../../src/utils/state-util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAGhE,wEAAwE;AACxE,MAAM,UAAU,WAAW,CAAC,KAA6B;IACvD,QAAQ,KAAK,EAAE,CAAC;QAChB,KAAK,aAAa,CAAC,SAAS,CAAC,CAAC,OAAO,WAAW,CAAC;QACjD,KAAK,aAAa,CAAC,IAAI,CAAC,CAAC,OAAO,MAAM,CAAC;QACvC,KAAK,aAAa,CAAC,IAAI,CAAC,CAAC,OAAO,MAAM,CAAC;QACvC,KAAK,aAAa,CAAC,KAAK,CAAC,CAAC,OAAO,OAAO,CAAC;QACzC,KAAK,aAAa,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,CAAC;QACrC,KAAK,MAAM,CAAC,CAAC,OAAO,MAAM,CAAC;QAC3B,KAAK,SAAS,CAAC,CAAC,OAAO,SAAS,CAAC;QACjC,OAAO,CAAC,CAAC,OAAO,SAAS,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,QAAQ,IAAI,EAAE,CAAC;QACf,KAAK,MAAM,CAAC,CAAC,OAAO,aAAa,CAAC,IAAI,CAAC;QACvC,KAAK,MAAM,CAAC,CAAC,OAAO,aAAa,CAAC,IAAI,CAAC;QACvC,KAAK,OAAO,CAAC,CAAC,OAAO,aAAa,CAAC,KAAK,CAAC;QACzC,KAAK,KAAK,CAAC,CAAC,OAAO,aAAa,CAAC,GAAG,CAAC;QACrC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAkB,CAAC;IACpC,CAAC;AACH,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,35 @@
1
+ import eslint from '@eslint/js';
2
+ import tseslint from 'typescript-eslint';
3
+
4
+ export default tseslint.config(
5
+ {
6
+ ignores: ['dist/**'],
7
+ },
8
+ {
9
+ rules: {
10
+ 'quotes': ['error', 'single'],
11
+ 'indent': ['error', 2, { 'SwitchCase': 0 }],
12
+ 'linebreak-style': ['error', 'unix'],
13
+ 'semi': ['error', 'always'],
14
+ 'comma-dangle': ['error', 'always-multiline'],
15
+ 'dot-notation': 'error',
16
+ 'eqeqeq': ['error', 'smart'],
17
+ 'curly': ['error', 'all'],
18
+ 'brace-style': ['error'],
19
+ 'prefer-arrow-callback': 'warn',
20
+ 'max-len': ['warn', 160],
21
+ 'object-curly-spacing': ['error', 'always'],
22
+ 'no-use-before-define': 'off',
23
+ '@typescript-eslint/no-use-before-define': ['error', { 'classes': false, 'enums': false }],
24
+ '@typescript-eslint/no-unused-vars': ['error', { 'caughtErrors': 'none' }],
25
+ },
26
+ },
27
+ {
28
+ languageOptions: {
29
+ ecmaVersion: 2022,
30
+ sourceType: 'module',
31
+ },
32
+ },
33
+ eslint.configs.recommended,
34
+ ...tseslint.configs.recommended,
35
+ );
package/package.json CHANGED
@@ -1,13 +1,18 @@
1
1
  {
2
2
  "name": "homebridge-securitysystem",
3
3
  "displayName": "Homebridge Security System",
4
- "version": "9.1.2",
4
+ "type": "module",
5
+ "version": "10.0.0-beta.2",
5
6
  "description": "Homebridge plugin that creates a security system accessory that can be triggered by HomeKit sensors.",
6
- "main": "src/index.js",
7
- "scripts": {
8
- "debug": "homebridge -I -D",
9
- "test": "exit 0",
10
- "build": "exit 0"
7
+ "author": "MiguelRipoll23",
8
+ "license": "MIT",
9
+ "homepage": "https://github.com/MiguelRipoll23/homebridge-securitysystem#readme",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/MiguelRipoll23/homebridge-securitysystem.git"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/MiguelRipoll23/homebridge-securitysystem/issues"
11
16
  },
12
17
  "keywords": [
13
18
  "homebridge-plugin",
@@ -17,39 +22,40 @@
17
22
  "alarm",
18
23
  "category-security"
19
24
  ],
20
- "repository": {
21
- "type": "git",
22
- "url": "git://github.com/MiguelRipoll23/homebridge-securitysystem.git"
25
+ "main": "dist/index.js",
26
+ "engines": {
27
+ "node": "^20.18.0 || ^22.10.0 || ^24.0.0",
28
+ "homebridge": "^1.8.0 || ^2.0.0"
23
29
  },
24
- "author": "MiguelRipoll23",
25
- "license": "MIT",
26
- "bugs": {
27
- "url": "http://github.com/MiguelRipoll23/homebridge-securitysystem/issues"
30
+ "scripts": {
31
+ "build": "rimraf ./dist && tsc",
32
+ "lint": "eslint . --max-warnings=0",
33
+ "test": "vitest run",
34
+ "prepublishOnly": "npm run lint && npm run build",
35
+ "watch": "npm run build && npm link && nodemon"
36
+ },
37
+ "dependencies": {
38
+ "@hono/node-server": "^1.19.13",
39
+ "hono": "^4.12.12",
40
+ "node-persist": "^4.0.1"
41
+ },
42
+ "devDependencies": {
43
+ "@eslint/js": "^9.39.1",
44
+ "@types/node": "^24.10.1",
45
+ "@types/node-persist": "^3.1.8",
46
+ "eslint": "^9.39.1",
47
+ "homebridge": "^2.0.0-beta.87",
48
+ "nodemon": "^3.1.11",
49
+ "rimraf": "^6.1.0",
50
+ "ts-node": "^10.9.2",
51
+ "typescript": "^5.9.3",
52
+ "typescript-eslint": "^8.46.4",
53
+ "vitest": "^3.0.0"
28
54
  },
29
55
  "funding": [
30
56
  {
31
57
  "type": "github",
32
58
  "url": "https://github.com/sponsors/MiguelRipoll23"
33
- },
34
- {
35
- "type": "buymeacoffee",
36
- "url": "https://www.buymeacoffee.com/MiguelRipoll23"
37
- },
38
- {
39
- "type": "paypal",
40
- "url": "https://paypal.me/miguelripoll23"
41
59
  }
42
- ],
43
- "engines": {
44
- "node": "^18.15.0 || ^20.7.0 || ^22 || ^24",
45
- "homebridge": "^1.6.0 || ^2.0.0"
46
- },
47
- "dependencies": {
48
- "express": "^5.1.0",
49
- "node-fetch": "^2.6.7",
50
- "node-persist": "^4.0.1"
51
- },
52
- "devDependencies": {
53
- "eslint": "^10.0.0"
54
- }
60
+ ]
55
61
  }
@@ -0,0 +1,14 @@
1
+ declare module 'homebridge-lib/EveHomeKitTypes' {
2
+ export class EveHomeKitTypes {
3
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
+ constructor(homebridge: any);
5
+
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ Characteristics: Record<string, any>;
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ Services: Record<string, any>;
10
+ }
11
+ }
12
+
13
+ declare module 'homebridge-lib' {
14
+ }
@@ -0,0 +1,18 @@
1
+ import { SecurityState } from '../types/security-state-type.js';
2
+ import type { ConditionContext } from '../interfaces/condition-context-interface.js';
3
+ import { Condition } from './condition.js';
4
+
5
+ /**
6
+ * Blocks a new trip activation when the alarm has already been triggered.
7
+ * Prevents stacking multiple trigger events on an active alarm.
8
+ */
9
+ export class AlreadyTriggeredCondition extends Condition {
10
+ readonly name = 'already-triggered';
11
+
12
+ evaluate({ state, value }: ConditionContext): boolean {
13
+ if (!value) {
14
+ return false;
15
+ }
16
+ return state.currentState === SecurityState.TRIGGERED;
17
+ }
18
+ }
@@ -0,0 +1,17 @@
1
+ import type { ConditionContext } from '../interfaces/condition-context-interface.js';
2
+ import { Condition } from './condition.js';
3
+
4
+ /**
5
+ * Blocks a trip action while the arm delay countdown is still running.
6
+ * Prevents an intruder from tripping the system before it has fully armed.
7
+ */
8
+ export class ArmingInProgressCondition extends Condition {
9
+ readonly name = 'arming-in-progress';
10
+
11
+ evaluate({ state, value }: ConditionContext): boolean {
12
+ if (!value) {
13
+ return false;
14
+ }
15
+ return state.isArming;
16
+ }
17
+ }
@@ -0,0 +1,48 @@
1
+ import { SecurityState } from '../types/security-state-type.js';
2
+ import type { ConditionContext } from '../interfaces/condition-context-interface.js';
3
+ import type { CharacteristicConstructor } from '../interfaces/hap-types-interface.js';
4
+ import { Condition } from './condition.js';
5
+
6
+ /**
7
+ * Blocks arming when an arming-lock switch (global or mode-specific) is active.
8
+ * Does NOT block disarming.
9
+ */
10
+ export class ArmingLockCondition extends Condition {
11
+ readonly name = 'arming-lock';
12
+
13
+ constructor(private readonly Characteristic: CharacteristicConstructor) {
14
+ super();
15
+ }
16
+
17
+ evaluate({ state, services, options }: ConditionContext): boolean {
18
+ const hasLockFeature = options.armingLockSwitch || options.armingLockSwitches;
19
+ if (!hasLockFeature) {
20
+ return false;
21
+ }
22
+
23
+ const targetState = state.targetState;
24
+ if (targetState === SecurityState.OFF) {
25
+ return false;
26
+ }
27
+
28
+ // Check global arming-lock switch.
29
+ const globalOn = services.armingLockSwitchService
30
+ .getCharacteristic(this.Characteristic.On).value;
31
+
32
+ if (globalOn) {
33
+ return true;
34
+ }
35
+
36
+ // Check mode-specific arming-lock switch.
37
+ switch (targetState) {
38
+ case SecurityState.HOME:
39
+ return Boolean(services.armingLockHomeSwitchService.getCharacteristic(this.Characteristic.On).value);
40
+ case SecurityState.AWAY:
41
+ return Boolean(services.armingLockAwaySwitchService.getCharacteristic(this.Characteristic.On).value);
42
+ case SecurityState.NIGHT:
43
+ return Boolean(services.armingLockNightSwitchService.getCharacteristic(this.Characteristic.On).value);
44
+ default:
45
+ return false;
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,17 @@
1
+ import type { ConditionContext } from '../interfaces/condition-context-interface.js';
2
+
3
+ /**
4
+ * Base class for all conditions that can block a trip/trigger action.
5
+ *
6
+ * Extend this class and implement `evaluate()` to return `true` when the
7
+ * condition should prevent the action from proceeding.
8
+ */
9
+ export abstract class Condition {
10
+ abstract readonly name: string;
11
+
12
+ /**
13
+ * Returns `true` if this condition blocks the action described by `context`.
14
+ * When `true` is returned the caller must reject the HomeKit request.
15
+ */
16
+ abstract evaluate(context: ConditionContext): boolean;
17
+ }
@@ -0,0 +1,82 @@
1
+ import { SecurityState } from '../types/security-state-type.js';
2
+ import { OriginType } from '../types/origin-type.js';
3
+ import type { ConditionContext } from '../interfaces/condition-context-interface.js';
4
+ import { modeToState } from '../utils/state-util.js';
5
+ import { Condition } from './condition.js';
6
+
7
+ /**
8
+ * Implements the double-knock pattern: the first activation within a mode that
9
+ * has double-knock enabled sets a "knocked" flag and rejects the trip. Only the
10
+ * second activation within the configured window proceeds.
11
+ *
12
+ * The caller is responsible for managing `state.isKnocked` and the timeout
13
+ * via the `onFirstKnock` callback injected at construction.
14
+ */
15
+ export class DoubleKnockCondition extends Condition {
16
+ readonly name = 'double-knock';
17
+
18
+ /**
19
+ * @param onFirstKnock Called when the first knock is detected.
20
+ * The callback receives the window duration in seconds and a reset function
21
+ * to invoke when the window expires.
22
+ */
23
+ constructor(
24
+ private readonly onFirstKnock: (seconds: number, onExpire: () => void) => void,
25
+ ) {
26
+ super();
27
+ }
28
+
29
+ evaluate({ state, options, value, origin }: ConditionContext): boolean {
30
+ if (!value || !options.doubleKnock) {
31
+ return false;
32
+ }
33
+ if (origin === OriginType.OVERRIDE_SWITCH) {
34
+ return false;
35
+ }
36
+ if (state.isArming) {
37
+ return false;
38
+ }
39
+
40
+ const knockableStates = options.doubleKnockModes.map(m => modeToState(m.toLowerCase()));
41
+ if (!knockableStates.includes(state.currentState)) {
42
+ return false;
43
+ }
44
+
45
+ if (state.isKnocked) {
46
+ // Second knock — clear the flag and allow through.
47
+ state.isKnocked = false;
48
+ if (state.doubleKnockTimeout !== null) {
49
+ clearTimeout(state.doubleKnockTimeout);
50
+ state.doubleKnockTimeout = null;
51
+ }
52
+ return false;
53
+ }
54
+
55
+ // First knock — block and schedule expiry.
56
+ state.isKnocked = true;
57
+ const seconds = this.resolveWindow(state.currentState, options);
58
+
59
+ this.onFirstKnock(seconds, () => {
60
+ state.isKnocked = false;
61
+ state.doubleKnockTimeout = null;
62
+ });
63
+
64
+ return true;
65
+ }
66
+
67
+ private resolveWindow(
68
+ currentState: SecurityState,
69
+ options: ConditionContext['options'],
70
+ ): number {
71
+ if (currentState === SecurityState.HOME && options.homeDoubleKnockSeconds !== null) {
72
+ return options.homeDoubleKnockSeconds;
73
+ }
74
+ if (currentState === SecurityState.AWAY && options.awayDoubleKnockSeconds !== null) {
75
+ return options.awayDoubleKnockSeconds;
76
+ }
77
+ if (currentState === SecurityState.NIGHT && options.nightDoubleKnockSeconds !== null) {
78
+ return options.nightDoubleKnockSeconds;
79
+ }
80
+ return options.doubleKnockSeconds;
81
+ }
82
+ }
@@ -0,0 +1,24 @@
1
+ import { SecurityState } from '../types/security-state-type.js';
2
+ import { OriginType } from '../types/origin-type.js';
3
+ import type { ConditionContext } from '../interfaces/condition-context-interface.js';
4
+ import { Condition } from './condition.js';
5
+
6
+ /**
7
+ * Blocks a trip action when the system is disarmed and neither
8
+ * `override_off` nor the override switch is in use.
9
+ */
10
+ export class NotArmedCondition extends Condition {
11
+ readonly name = 'not-armed';
12
+
13
+ evaluate({ state, options, value, origin }: ConditionContext): boolean {
14
+ if (!value) {
15
+ return false;
16
+ }
17
+
18
+ const isDisarmed = state.currentState === SecurityState.OFF;
19
+ const isNotOverridingOff = !options.overrideOff;
20
+ const isNotOverrideSwitch = origin !== OriginType.OVERRIDE_SWITCH;
21
+
22
+ return isDisarmed && isNotOverridingOff && isNotOverrideSwitch;
23
+ }
24
+ }
@@ -0,0 +1,28 @@
1
+ /** Default configuration values applied when a config property is not set. */
2
+ export const DEFAULTS = {
3
+ NAME: 'Security System',
4
+ SERIAL_NUMBER: 'S3CUR1TYSYST3M',
5
+ DEFAULT_MODE: 'off',
6
+ ARM_SECONDS: 0,
7
+ TRIGGER_SECONDS: 0,
8
+ RESET_MINUTES: 10,
9
+ DOUBLE_KNOCK_SECONDS: 90,
10
+ TRIPPED_SENSOR_SECONDS: 5,
11
+ TRIGGERED_SENSOR_SECONDS: 5,
12
+ PAUSE_MINUTES: 0,
13
+ AUDIO_LANGUAGE: 'en-US',
14
+
15
+ // Switch display names
16
+ TRIP_SWITCH_NAME: 'Trip',
17
+ TRIP_HOME_SWITCH_NAME: 'Trip Home',
18
+ TRIP_AWAY_SWITCH_NAME: 'Trip Away',
19
+ TRIP_NIGHT_SWITCH_NAME: 'Trip Night',
20
+ TRIP_OVERRIDE_SWITCH_NAME: 'Trip Override',
21
+ MODE_HOME_SWITCH_NAME: 'Mode Home',
22
+ MODE_AWAY_SWITCH_NAME: 'Mode Away',
23
+ MODE_NIGHT_SWITCH_NAME: 'Mode Night',
24
+ MODE_OFF_SWITCH_NAME: 'Mode Off',
25
+ MODE_AWAY_EXTENDED_SWITCH_NAME: 'Mode Away Extended',
26
+ MODE_PAUSE_SWITCH_NAME: 'Mode Pause',
27
+ AUDIO_SWITCH_NAME: 'Audio',
28
+ } as const;
@@ -0,0 +1,5 @@
1
+ /** HomeKit HAP status code returned when an action is not permitted in the current state. */
2
+ export const HK_NOT_ALLOWED_IN_CURRENT_STATE = -70412;
3
+
4
+ /** Pulse duration in ms used by motion sensors to emit a brief motion event. */
5
+ export const SENSOR_PULSE_MS = 750;
@@ -0,0 +1,23 @@
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
+ } as const;