homebridge-securitysystem 0.0.1-matter

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 (257) hide show
  1. package/.gitattributes +1 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.yml +46 -0
  3. package/.github/ISSUE_TEMPLATE/feature-request.yml +17 -0
  4. package/.github/ISSUE_TEMPLATE/questions-answers.yml +40 -0
  5. package/.github/demo.jpg +0 -0
  6. package/.github/dependabot.yml +19 -0
  7. package/.github/release.yml +36 -0
  8. package/.github/workflows/build-package.yml +49 -0
  9. package/.github/workflows/bump-version.yml +59 -0
  10. package/.github/workflows/dependency-review.yml +26 -0
  11. package/.github/workflows/publish.yml +155 -0
  12. package/.github/workflows/stale-activity.yml +27 -0
  13. package/AGENTS.md +247 -0
  14. package/CHANGELOG.md +572 -0
  15. package/CODE_OF_CONDUCT.md +128 -0
  16. package/CONTRIBUTING.md +5 -0
  17. package/LICENSE +21 -0
  18. package/README.md +47 -0
  19. package/SECURITY.md +12 -0
  20. package/config.schema.json +934 -0
  21. package/dist/conditions/already-triggered-condition.d.ts +10 -0
  22. package/dist/conditions/already-triggered-condition.js +22 -0
  23. package/dist/conditions/already-triggered-condition.js.map +1 -0
  24. package/dist/conditions/arming-in-progress-condition.d.ts +10 -0
  25. package/dist/conditions/arming-in-progress-condition.js +21 -0
  26. package/dist/conditions/arming-in-progress-condition.js.map +1 -0
  27. package/dist/conditions/arming-lock-condition.d.ts +10 -0
  28. package/dist/conditions/arming-lock-condition.js +43 -0
  29. package/dist/conditions/arming-lock-condition.js.map +1 -0
  30. package/dist/conditions/condition.d.ts +22 -0
  31. package/dist/conditions/condition.js +19 -0
  32. package/dist/conditions/condition.js.map +1 -0
  33. package/dist/conditions/double-knock-condition.d.ts +24 -0
  34. package/dist/conditions/double-knock-condition.js +73 -0
  35. package/dist/conditions/double-knock-condition.js.map +1 -0
  36. package/dist/conditions/not-armed-condition.d.ts +10 -0
  37. package/dist/conditions/not-armed-condition.js +26 -0
  38. package/dist/conditions/not-armed-condition.js.map +1 -0
  39. package/dist/conditions/trigger-already-running-condition.d.ts +10 -0
  40. package/dist/conditions/trigger-already-running-condition.js +21 -0
  41. package/dist/conditions/trigger-already-running-condition.js.map +1 -0
  42. package/dist/constants/default-constant.d.ts +24 -0
  43. package/dist/constants/default-constant.js +27 -0
  44. package/dist/constants/default-constant.js.map +1 -0
  45. package/dist/constants/homekit-constant.d.ts +2 -0
  46. package/dist/constants/homekit-constant.js +3 -0
  47. package/dist/constants/homekit-constant.js.map +1 -0
  48. package/dist/constants/matter-constant.d.ts +21 -0
  49. package/dist/constants/matter-constant.js +61 -0
  50. package/dist/constants/matter-constant.js.map +1 -0
  51. package/dist/handlers/sensor-handler.d.ts +24 -0
  52. package/dist/handlers/sensor-handler.js +63 -0
  53. package/dist/handlers/sensor-handler.js.map +1 -0
  54. package/dist/handlers/state-handler.d.ts +49 -0
  55. package/dist/handlers/state-handler.js +204 -0
  56. package/dist/handlers/state-handler.js.map +1 -0
  57. package/dist/handlers/switch-handler.d.ts +28 -0
  58. package/dist/handlers/switch-handler.js +95 -0
  59. package/dist/handlers/switch-handler.js.map +1 -0
  60. package/dist/handlers/trip-handler.d.ts +44 -0
  61. package/dist/handlers/trip-handler.js +156 -0
  62. package/dist/handlers/trip-handler.js.map +1 -0
  63. package/dist/homekit/homekit-registrar.d.ts +12 -0
  64. package/dist/homekit/homekit-registrar.js +23 -0
  65. package/dist/homekit/homekit-registrar.js.map +1 -0
  66. package/dist/homekit/service-factory.d.ts +12 -0
  67. package/dist/homekit/service-factory.js +24 -0
  68. package/dist/homekit/service-factory.js.map +1 -0
  69. package/dist/index.d.ts +3 -0
  70. package/dist/index.js +7 -0
  71. package/dist/index.js.map +1 -0
  72. package/dist/interfaces/condition-context-interface.d.ts +14 -0
  73. package/dist/interfaces/condition-context-interface.js +2 -0
  74. package/dist/interfaces/condition-context-interface.js.map +1 -0
  75. package/dist/interfaces/hap-types-interface.d.ts +9 -0
  76. package/dist/interfaces/hap-types-interface.js +2 -0
  77. package/dist/interfaces/hap-types-interface.js.map +1 -0
  78. package/dist/interfaces/mqtt-status-payload-interface.d.ts +6 -0
  79. package/dist/interfaces/mqtt-status-payload-interface.js +2 -0
  80. package/dist/interfaces/mqtt-status-payload-interface.js.map +1 -0
  81. package/dist/interfaces/options-interface.d.ts +87 -0
  82. package/dist/interfaces/options-interface.js +2 -0
  83. package/dist/interfaces/options-interface.js.map +1 -0
  84. package/dist/interfaces/persisted-state-interface.d.ts +6 -0
  85. package/dist/interfaces/persisted-state-interface.js +2 -0
  86. package/dist/interfaces/persisted-state-interface.js.map +1 -0
  87. package/dist/interfaces/service-registry-interface.d.ts +10 -0
  88. package/dist/interfaces/service-registry-interface.js +2 -0
  89. package/dist/interfaces/service-registry-interface.js.map +1 -0
  90. package/dist/interfaces/system-state-interface.d.ts +22 -0
  91. package/dist/interfaces/system-state-interface.js +2 -0
  92. package/dist/interfaces/system-state-interface.js.map +1 -0
  93. package/dist/schemas/arming-lock-schema.d.ts +10 -0
  94. package/dist/schemas/arming-lock-schema.js +12 -0
  95. package/dist/schemas/arming-lock-schema.js.map +1 -0
  96. package/dist/schemas/error-schema.d.ts +4 -0
  97. package/dist/schemas/error-schema.js +7 -0
  98. package/dist/schemas/error-schema.js.map +1 -0
  99. package/dist/schemas/mode-request-schema.d.ts +10 -0
  100. package/dist/schemas/mode-request-schema.js +18 -0
  101. package/dist/schemas/mode-request-schema.js.map +1 -0
  102. package/dist/schemas/status-response-schema.d.ts +7 -0
  103. package/dist/schemas/status-response-schema.js +10 -0
  104. package/dist/schemas/status-response-schema.js.map +1 -0
  105. package/dist/schemas/trip-mode-request-schema.d.ts +9 -0
  106. package/dist/schemas/trip-mode-request-schema.js +24 -0
  107. package/dist/schemas/trip-mode-request-schema.js.map +1 -0
  108. package/dist/security-system-platform.d.ts +19 -0
  109. package/dist/security-system-platform.js +56 -0
  110. package/dist/security-system-platform.js.map +1 -0
  111. package/dist/security-system.d.ts +28 -0
  112. package/dist/security-system.js +169 -0
  113. package/dist/security-system.js.map +1 -0
  114. package/dist/services/command-service.d.ts +16 -0
  115. package/dist/services/command-service.js +65 -0
  116. package/dist/services/command-service.js.map +1 -0
  117. package/dist/services/configuration-service.d.ts +24 -0
  118. package/dist/services/configuration-service.js +189 -0
  119. package/dist/services/configuration-service.js.map +1 -0
  120. package/dist/services/event-bus-service.d.ts +12 -0
  121. package/dist/services/event-bus-service.js +17 -0
  122. package/dist/services/event-bus-service.js.map +1 -0
  123. package/dist/services/matter-service.d.ts +69 -0
  124. package/dist/services/matter-service.js +363 -0
  125. package/dist/services/matter-service.js.map +1 -0
  126. package/dist/services/mqtt-service.d.ts +16 -0
  127. package/dist/services/mqtt-service.js +97 -0
  128. package/dist/services/mqtt-service.js.map +1 -0
  129. package/dist/services/server-service.d.ts +28 -0
  130. package/dist/services/server-service.js +275 -0
  131. package/dist/services/server-service.js.map +1 -0
  132. package/dist/services/storage-service.d.ts +14 -0
  133. package/dist/services/storage-service.js +70 -0
  134. package/dist/services/storage-service.js.map +1 -0
  135. package/dist/services/webhook-service.d.ts +16 -0
  136. package/dist/services/webhook-service.js +68 -0
  137. package/dist/services/webhook-service.js.map +1 -0
  138. package/dist/tests/conditions.test.d.ts +1 -0
  139. package/dist/tests/conditions.test.js +175 -0
  140. package/dist/tests/conditions.test.js.map +1 -0
  141. package/dist/tests/matter-service.test.d.ts +1 -0
  142. package/dist/tests/matter-service.test.js +475 -0
  143. package/dist/tests/matter-service.test.js.map +1 -0
  144. package/dist/tests/mqtt-service.test.d.ts +1 -0
  145. package/dist/tests/mqtt-service.test.js +168 -0
  146. package/dist/tests/mqtt-service.test.js.map +1 -0
  147. package/dist/tests/sensor-handler.test.d.ts +1 -0
  148. package/dist/tests/sensor-handler.test.js +87 -0
  149. package/dist/tests/sensor-handler.test.js.map +1 -0
  150. package/dist/tests/server-service.test.d.ts +1 -0
  151. package/dist/tests/server-service.test.js +188 -0
  152. package/dist/tests/server-service.test.js.map +1 -0
  153. package/dist/tests/state-handler.test.d.ts +1 -0
  154. package/dist/tests/state-handler.test.js +276 -0
  155. package/dist/tests/state-handler.test.js.map +1 -0
  156. package/dist/tests/switch-handler.test.d.ts +1 -0
  157. package/dist/tests/switch-handler.test.js +186 -0
  158. package/dist/tests/switch-handler.test.js.map +1 -0
  159. package/dist/tests/trip-handler.test.d.ts +1 -0
  160. package/dist/tests/trip-handler.test.js +179 -0
  161. package/dist/tests/trip-handler.test.js.map +1 -0
  162. package/dist/timers/timer-manager.d.ts +31 -0
  163. package/dist/timers/timer-manager.js +118 -0
  164. package/dist/timers/timer-manager.js.map +1 -0
  165. package/dist/types/event-payload-map-type.d.ts +15 -0
  166. package/dist/types/event-payload-map-type.js +2 -0
  167. package/dist/types/event-payload-map-type.js.map +1 -0
  168. package/dist/types/event-type.d.ts +44 -0
  169. package/dist/types/event-type.js +20 -0
  170. package/dist/types/event-type.js.map +1 -0
  171. package/dist/types/matter-accessory-key-type.d.ts +7 -0
  172. package/dist/types/matter-accessory-key-type.js +2 -0
  173. package/dist/types/matter-accessory-key-type.js.map +1 -0
  174. package/dist/types/mode-type.d.ts +4 -0
  175. package/dist/types/mode-type.js +2 -0
  176. package/dist/types/mode-type.js.map +1 -0
  177. package/dist/types/origin-type.d.ts +9 -0
  178. package/dist/types/origin-type.js +11 -0
  179. package/dist/types/origin-type.js.map +1 -0
  180. package/dist/types/security-state-type.d.ts +11 -0
  181. package/dist/types/security-state-type.js +13 -0
  182. package/dist/types/security-state-type.js.map +1 -0
  183. package/dist/types/sensor-kind-type.d.ts +2 -0
  184. package/dist/types/sensor-kind-type.js +2 -0
  185. package/dist/types/sensor-kind-type.js.map +1 -0
  186. package/dist/types/service-result-type.d.ts +4 -0
  187. package/dist/types/service-result-type.js +2 -0
  188. package/dist/types/service-result-type.js.map +1 -0
  189. package/dist/utils/arming-util.d.ts +19 -0
  190. package/dist/utils/arming-util.js +24 -0
  191. package/dist/utils/arming-util.js.map +1 -0
  192. package/dist/utils/state-util.d.ts +8 -0
  193. package/dist/utils/state-util.js +29 -0
  194. package/dist/utils/state-util.js.map +1 -0
  195. package/docs/MQTT.md +50 -0
  196. package/package.json +64 -0
  197. package/src/@types/homebridge-lib.d.ts +11 -0
  198. package/src/conditions/already-triggered-condition.ts +24 -0
  199. package/src/conditions/arming-in-progress-condition.ts +23 -0
  200. package/src/conditions/arming-lock-condition.ts +50 -0
  201. package/src/conditions/condition.ts +31 -0
  202. package/src/conditions/double-knock-condition.ts +84 -0
  203. package/src/conditions/not-armed-condition.ts +30 -0
  204. package/src/conditions/trigger-already-running-condition.ts +23 -0
  205. package/src/constants/default-constant.ts +28 -0
  206. package/src/constants/homekit-constant.ts +2 -0
  207. package/src/constants/matter-constant.ts +69 -0
  208. package/src/handlers/sensor-handler.ts +78 -0
  209. package/src/handlers/state-handler.ts +257 -0
  210. package/src/handlers/switch-handler.ts +111 -0
  211. package/src/handlers/trip-handler.ts +193 -0
  212. package/src/homekit/homekit-registrar.ts +28 -0
  213. package/src/homekit/service-factory.ts +35 -0
  214. package/src/index.ts +9 -0
  215. package/src/interfaces/condition-context-interface.ts +15 -0
  216. package/src/interfaces/hap-types-interface.ts +11 -0
  217. package/src/interfaces/mqtt-status-payload-interface.ts +6 -0
  218. package/src/interfaces/options-interface.ts +116 -0
  219. package/src/interfaces/persisted-state-interface.ts +7 -0
  220. package/src/interfaces/service-registry-interface.ts +11 -0
  221. package/src/interfaces/system-state-interface.ts +25 -0
  222. package/src/schemas/arming-lock-schema.ts +12 -0
  223. package/src/schemas/error-schema.ts +7 -0
  224. package/src/schemas/mode-request-schema.ts +18 -0
  225. package/src/schemas/status-response-schema.ts +10 -0
  226. package/src/schemas/trip-mode-request-schema.ts +24 -0
  227. package/src/security-system-platform.ts +68 -0
  228. package/src/security-system.ts +199 -0
  229. package/src/services/command-service.ts +76 -0
  230. package/src/services/configuration-service.ts +224 -0
  231. package/src/services/event-bus-service.ts +21 -0
  232. package/src/services/matter-service.ts +446 -0
  233. package/src/services/mqtt-service.ts +123 -0
  234. package/src/services/server-service.ts +327 -0
  235. package/src/services/storage-service.ts +80 -0
  236. package/src/services/webhook-service.ts +77 -0
  237. package/src/tests/conditions.test.ts +212 -0
  238. package/src/tests/matter-service.test.ts +610 -0
  239. package/src/tests/mqtt-service.test.ts +231 -0
  240. package/src/tests/sensor-handler.test.ts +116 -0
  241. package/src/tests/server-service.test.ts +238 -0
  242. package/src/tests/state-handler.test.ts +371 -0
  243. package/src/tests/switch-handler.test.ts +234 -0
  244. package/src/tests/trip-handler.test.ts +244 -0
  245. package/src/timers/timer-manager.ts +141 -0
  246. package/src/types/event-payload-map-type.ts +25 -0
  247. package/src/types/event-type.ts +61 -0
  248. package/src/types/matter-accessory-key-type.ts +33 -0
  249. package/src/types/mode-type.ts +4 -0
  250. package/src/types/origin-type.ts +9 -0
  251. package/src/types/security-state-type.ts +11 -0
  252. package/src/types/sensor-kind-type.ts +2 -0
  253. package/src/types/service-result-type.ts +4 -0
  254. package/src/utils/arming-util.ts +44 -0
  255. package/src/utils/state-util.ts +32 -0
  256. package/tsconfig.json +26 -0
  257. package/vitest.config.ts +8 -0
@@ -0,0 +1,276 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { SecurityState } from '../types/security-state-type.js';
3
+ import { OriginType } from '../types/origin-type.js';
4
+ import { EventType } from '../types/event-type.js';
5
+ // ── Minimal mocks ─────────────────────────────────────────────────────────────
6
+ function makeMockChar(initialValue = false) {
7
+ const char = { value: initialValue, updateValue: vi.fn(), setProps: vi.fn() };
8
+ char.updateValue.mockImplementation((v) => {
9
+ char.value = v;
10
+ });
11
+ return char;
12
+ }
13
+ function makeMockService(charValue = false) {
14
+ const char = makeMockChar(charValue);
15
+ return {
16
+ getCharacteristic: vi.fn().mockReturnValue(char),
17
+ setCharacteristic: vi.fn().mockReturnThis(),
18
+ updateCharacteristic: vi.fn(),
19
+ addCharacteristic: vi.fn(),
20
+ addOptionalCharacteristic: vi.fn(),
21
+ };
22
+ }
23
+ function makeServices() {
24
+ const s = {};
25
+ const keys = [
26
+ 'mainService', 'accessoryInfoService', 'tripSwitchService', 'tripHomeSwitchService',
27
+ 'tripAwaySwitchService', 'tripNightSwitchService', 'tripOverrideSwitchService',
28
+ 'armingLockSwitchService', 'armingLockHomeSwitchService', 'armingLockAwaySwitchService',
29
+ 'armingLockNightSwitchService', 'modeHomeSwitchService', 'modeAwaySwitchService',
30
+ 'modeNightSwitchService', 'modeOffSwitchService', 'modeAwayExtendedSwitchService',
31
+ 'modePauseSwitchService', 'armingMotionSensorService',
32
+ 'trippedMotionSensorService', 'triggeredResetMotionSensorService',
33
+ ];
34
+ for (const k of keys) {
35
+ s[k] = makeMockService();
36
+ }
37
+ return s;
38
+ }
39
+ function makeState(overrides = {}) {
40
+ return {
41
+ currentState: SecurityState.OFF,
42
+ targetState: SecurityState.OFF,
43
+ defaultState: SecurityState.OFF,
44
+ availableTargetStates: [SecurityState.HOME, SecurityState.AWAY, SecurityState.NIGHT, SecurityState.OFF],
45
+ isArming: false,
46
+ isTripping: false,
47
+ isKnocked: false,
48
+ serverAuthenticationAttempts: 0,
49
+ pausedCurrentState: null,
50
+ armingLocks: { global: false, home: false, away: false, night: false },
51
+ modeAwayExtended: false,
52
+ ...overrides,
53
+ };
54
+ }
55
+ function makeOptions(overrides = {}) {
56
+ return {
57
+ armSeconds: 0,
58
+ triggerSeconds: 0,
59
+ resetMinutes: 10,
60
+ testMode: false,
61
+ proxyMode: false,
62
+ saveState: false,
63
+ armingLockSwitch: false,
64
+ armingLockSwitches: false,
65
+ disabledModes: [],
66
+ homeArmSeconds: null,
67
+ awayArmSeconds: null,
68
+ nightArmSeconds: null,
69
+ homeTriggerSeconds: null,
70
+ awayTriggerSeconds: null,
71
+ nightTriggerSeconds: null,
72
+ trippedMotionSensor: false,
73
+ trippedMotionSensorSeconds: 5,
74
+ resetOffFlow: false,
75
+ ...overrides,
76
+ };
77
+ }
78
+ function makeMockLog() {
79
+ return { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() };
80
+ }
81
+ function makeStorage() {
82
+ return { save: vi.fn(), load: vi.fn(), init: vi.fn() };
83
+ }
84
+ function makeTimers() {
85
+ return {
86
+ setArmTimer: vi.fn(), clearArmTimer: vi.fn(),
87
+ setTriggerTimer: vi.fn(), clearTriggerTimer: vi.fn(), isTriggerRunning: vi.fn().mockReturnValue(false),
88
+ setPauseTimer: vi.fn(), clearPauseTimer: vi.fn(),
89
+ setDoubleKnockTimer: vi.fn(), clearDoubleKnockTimer: vi.fn(),
90
+ setResetTimer: vi.fn(), clearResetTimer: vi.fn(),
91
+ setTrippedInterval: vi.fn(), clearTrippedInterval: vi.fn(),
92
+ clearAll: vi.fn(),
93
+ };
94
+ }
95
+ function makeMockSensor() {
96
+ return {
97
+ resetArmingMotionSensor: vi.fn(),
98
+ setTrippedMotionSensor: vi.fn(),
99
+ resetTrippedMotionSensor: vi.fn(),
100
+ pulseResetMotionSensor: vi.fn(),
101
+ updateArmingMotionSensor: vi.fn(),
102
+ };
103
+ }
104
+ // ── StateHandler tests ────────────────────────────────────────────────────────
105
+ describe('StateHandler.getArmingSeconds', async () => {
106
+ const { StateHandler } = await import('../handlers/state-handler.js');
107
+ let stateHandler;
108
+ let state;
109
+ beforeEach(async () => {
110
+ const { EventBusService } = await import('../services/event-bus-service.js');
111
+ state = makeState();
112
+ const services = makeServices();
113
+ const options = makeOptions();
114
+ const log = makeMockLog();
115
+ const bus = new EventBusService();
116
+ const sensor = makeMockSensor();
117
+ stateHandler = new StateHandler(services, state, options, {}, log, bus, makeStorage(), makeTimers(), sensor);
118
+ });
119
+ it('returns 0 when current state is TRIGGERED', () => {
120
+ state.currentState = SecurityState.TRIGGERED;
121
+ expect(stateHandler.getArmingSeconds(SecurityState.HOME)).toBe(0);
122
+ });
123
+ it('returns 0 when target state is OFF (disarm)', () => {
124
+ state.currentState = SecurityState.HOME;
125
+ expect(stateHandler.getArmingSeconds(SecurityState.OFF)).toBe(0);
126
+ });
127
+ it('returns global armSeconds when no mode-specific value', () => {
128
+ state.currentState = SecurityState.HOME;
129
+ expect(stateHandler.getArmingSeconds(SecurityState.AWAY)).toBe(0);
130
+ });
131
+ });
132
+ // ── StateHandler.updateTargetState ───────────────────────────────────────────
133
+ describe('StateHandler.updateTargetState', async () => {
134
+ const { StateHandler } = await import('../handlers/state-handler.js');
135
+ const { EventBusService } = await import('../services/event-bus-service.js');
136
+ it('returns success (no-op) when currentState already matches the requested mode', async () => {
137
+ const state = makeState({ currentState: SecurityState.HOME, targetState: SecurityState.HOME });
138
+ const log = makeMockLog();
139
+ const bus = new EventBusService();
140
+ const sensor = makeMockSensor();
141
+ const timers = makeTimers();
142
+ const handler = new StateHandler(makeServices(), state, makeOptions(), {}, log, bus, makeStorage(), timers, sensor);
143
+ const result = handler.updateTargetState(SecurityState.HOME, OriginType.INTERNAL, 0);
144
+ expect(result.success).toBe(true);
145
+ expect(timers.clearArmTimer).not.toHaveBeenCalled();
146
+ });
147
+ it('cancels arm timer and applies state immediately on same-target re-call while arming', async () => {
148
+ const state = makeState({ currentState: SecurityState.OFF, targetState: SecurityState.HOME, isArming: true });
149
+ const log = makeMockLog();
150
+ const bus = new EventBusService();
151
+ const sensor = makeMockSensor();
152
+ const timers = makeTimers();
153
+ const handler = new StateHandler(makeServices(), state, makeOptions(), {}, log, bus, makeStorage(), timers, sensor);
154
+ const result = handler.updateTargetState(SecurityState.HOME, OriginType.EXTERNAL, 0);
155
+ expect(result.success).toBe(true);
156
+ expect(timers.clearArmTimer).toHaveBeenCalled();
157
+ expect(state.isArming).toBe(false);
158
+ expect(state.currentState).toBe(SecurityState.HOME);
159
+ });
160
+ it('applies state immediately on same-target re-call when not arming', async () => {
161
+ const state = makeState({ currentState: SecurityState.OFF, targetState: SecurityState.HOME, isArming: false });
162
+ const log = makeMockLog();
163
+ const bus = new EventBusService();
164
+ const sensor = makeMockSensor();
165
+ const timers = makeTimers();
166
+ const handler = new StateHandler(makeServices(), state, makeOptions(), {}, log, bus, makeStorage(), timers, sensor);
167
+ const result = handler.updateTargetState(SecurityState.HOME, OriginType.EXTERNAL, 0);
168
+ expect(result.success).toBe(true);
169
+ expect(timers.clearArmTimer).not.toHaveBeenCalled();
170
+ expect(state.currentState).toBe(SecurityState.HOME);
171
+ });
172
+ it('transitions to a new armed mode', async () => {
173
+ const state = makeState({ currentState: SecurityState.OFF, targetState: SecurityState.OFF });
174
+ const log = makeMockLog();
175
+ const bus = new EventBusService();
176
+ const sensor = makeMockSensor();
177
+ const handler = new StateHandler(makeServices(), state, makeOptions(), {}, log, bus, makeStorage(), makeTimers(), sensor);
178
+ const result = handler.updateTargetState(SecurityState.HOME, OriginType.REGULAR_SWITCH, 0);
179
+ expect(result.success).toBe(true);
180
+ expect(state.targetState).toBe(SecurityState.HOME);
181
+ });
182
+ });
183
+ // ── Regression: RESET_TRIP_SWITCHES on TRIGGERED ───────────────────────────────
184
+ // Issue 905: Trip Switch was being reset to OFF when the alarm triggered,
185
+ // hiding the active sensor from the user.
186
+ describe('StateHandler.setCurrentState - RESET_TRIP_SWITCHES', async () => {
187
+ const { StateHandler } = await import('../handlers/state-handler.js');
188
+ const { EventBusService } = await import('../services/event-bus-service.js');
189
+ it('does not emit RESET_TRIP_SWITCHES when entering TRIGGERED', () => {
190
+ const state = makeState({ currentState: SecurityState.NIGHT });
191
+ const bus = new EventBusService();
192
+ const spy = vi.fn();
193
+ bus.on(EventType.RESET_TRIP_SWITCHES, spy);
194
+ const handler = new StateHandler(makeServices(), state, makeOptions(), {}, makeMockLog(), bus, makeStorage(), makeTimers(), makeMockSensor());
195
+ handler.setCurrentState(SecurityState.TRIGGERED, OriginType.EXTERNAL);
196
+ expect(spy).not.toHaveBeenCalled();
197
+ });
198
+ it('still emits RESET_TRIP_SWITCHES when entering a non-TRIGGERED state', () => {
199
+ const state = makeState({ currentState: SecurityState.TRIGGERED });
200
+ const bus = new EventBusService();
201
+ const spy = vi.fn();
202
+ bus.on(EventType.RESET_TRIP_SWITCHES, spy);
203
+ const handler = new StateHandler(makeServices(), state, makeOptions(), {}, makeMockLog(), bus, makeStorage(), makeTimers(), makeMockSensor());
204
+ handler.setCurrentState(SecurityState.NIGHT, OriginType.EXTERNAL);
205
+ expect(spy).toHaveBeenCalledTimes(1);
206
+ });
207
+ });
208
+ // ── Regression: isTripping reset on target state change ───────────────────────
209
+ // Issue 905: isTripping was not being reset when the user changed the target
210
+ // mode, causing stale trip state after OFF disarm + re-arm.
211
+ describe('StateHandler.updateTargetState - isTripping reset', async () => {
212
+ const { StateHandler } = await import('../handlers/state-handler.js');
213
+ const { EventBusService } = await import('../services/event-bus-service.js');
214
+ it('resets isTripping to false when target state changes to a different mode', () => {
215
+ const state = makeState({ currentState: SecurityState.HOME, isTripping: true });
216
+ const handler = new StateHandler(makeServices(), state, makeOptions(), {}, makeMockLog(), new EventBusService(), makeStorage(), makeTimers(), makeMockSensor());
217
+ handler.updateTargetState(SecurityState.AWAY, OriginType.INTERNAL, 0);
218
+ expect(state.isTripping).toBe(false);
219
+ });
220
+ it('resets isTripping to false when disarming from TRIGGERED to OFF', () => {
221
+ const state = makeState({
222
+ currentState: SecurityState.TRIGGERED,
223
+ targetState: SecurityState.NIGHT,
224
+ isTripping: true,
225
+ });
226
+ const handler = new StateHandler(makeServices(), state, makeOptions(), {}, makeMockLog(), new EventBusService(), makeStorage(), makeTimers(), makeMockSensor());
227
+ handler.updateTargetState(SecurityState.OFF, OriginType.INTERNAL, 0);
228
+ expect(state.isTripping).toBe(false);
229
+ });
230
+ });
231
+ // ── Arming locks read from SystemState (switches are Matter-only) ─────────────
232
+ describe('StateHandler.isArmingLocked', async () => {
233
+ const { StateHandler } = await import('../handlers/state-handler.js');
234
+ const { EventBusService } = await import('../services/event-bus-service.js');
235
+ function makeHandler(state) {
236
+ return new StateHandler(makeServices(), state, makeOptions({ armingLockSwitch: true }), {}, makeMockLog(), new EventBusService(), makeStorage(), makeTimers(), makeMockSensor());
237
+ }
238
+ it('blocks when the global arming lock is active', () => {
239
+ const state = makeState({ armingLocks: { global: true, home: false, away: false, night: false } });
240
+ expect(makeHandler(state).isArmingLocked(SecurityState.HOME)).toBe(true);
241
+ });
242
+ it('blocks when the matching mode lock is active', () => {
243
+ const state = makeState({ armingLocks: { global: false, home: false, away: true, night: false } });
244
+ expect(makeHandler(state).isArmingLocked(SecurityState.AWAY)).toBe(true);
245
+ expect(makeHandler(state).isArmingLocked(SecurityState.HOME)).toBe(false);
246
+ });
247
+ it('never blocks disarming', () => {
248
+ const state = makeState({ armingLocks: { global: true, home: false, away: false, night: false } });
249
+ expect(makeHandler(state).isArmingLocked(SecurityState.OFF)).toBe(false);
250
+ });
251
+ });
252
+ // ── Away-extended flag is cleared on any target change ───────────────────────
253
+ describe('StateHandler.updateTargetState - modeAwayExtended reset', async () => {
254
+ const { StateHandler } = await import('../handlers/state-handler.js');
255
+ const { EventBusService } = await import('../services/event-bus-service.js');
256
+ it('clears modeAwayExtended when the target changes', () => {
257
+ const state = makeState({ currentState: SecurityState.OFF, modeAwayExtended: true });
258
+ const handler = new StateHandler(makeServices(), state, makeOptions(), {}, makeMockLog(), new EventBusService(), makeStorage(), makeTimers(), makeMockSensor());
259
+ handler.updateTargetState(SecurityState.HOME, OriginType.INTERNAL, 0);
260
+ expect(state.modeAwayExtended).toBe(false);
261
+ });
262
+ });
263
+ // ── Tripped sensor reset on TRIGGERED ────────────────────────────────────────
264
+ describe('StateHandler.setCurrentState - tripped sensor reset', async () => {
265
+ const { StateHandler } = await import('../handlers/state-handler.js');
266
+ const { EventBusService } = await import('../services/event-bus-service.js');
267
+ it('resets tripped motion sensor when entering TRIGGERED', () => {
268
+ const state = makeState({ currentState: SecurityState.NIGHT });
269
+ const bus = new EventBusService();
270
+ const sensor = makeMockSensor();
271
+ const handler = new StateHandler(makeServices(), state, makeOptions(), {}, makeMockLog(), bus, makeStorage(), makeTimers(), sensor);
272
+ handler.setCurrentState(SecurityState.TRIGGERED, OriginType.EXTERNAL);
273
+ expect(sensor.resetTrippedMotionSensor).toHaveBeenCalled();
274
+ });
275
+ });
276
+ //# sourceMappingURL=state-handler.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-handler.test.js","sourceRoot":"","sources":["../../src/tests/state-handler.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAKrD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,iFAAiF;AAEjF,SAAS,YAAY,CAAC,YAAY,GAAY,KAAK;IACjD,MAAM,IAAI,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IAC9E,IAAI,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC,CAAU,EAAE,EAAE;QACjD,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,eAAe,CAAC,SAAS,GAAY,KAAK;IACjD,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IACrC,OAAO;QACL,iBAAiB,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;QAChD,iBAAiB,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,cAAc,EAAE;QAC3C,oBAAoB,EAAE,EAAE,CAAC,EAAE,EAAE;QAC7B,iBAAiB,EAAE,EAAE,CAAC,EAAE,EAAE;QAC1B,yBAAyB,EAAE,EAAE,CAAC,EAAE,EAAE;KACnC,CAAC;AACJ,CAAC;AAED,SAAS,YAAY;IACnB,MAAM,CAAC,GAAG,EAAwD,CAAC;IACnE,MAAM,IAAI,GAAG;QACX,aAAa,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,uBAAuB;QACnF,uBAAuB,EAAE,wBAAwB,EAAE,2BAA2B;QAC9E,yBAAyB,EAAE,6BAA6B,EAAE,6BAA6B;QACvF,8BAA8B,EAAE,uBAAuB,EAAE,uBAAuB;QAChF,wBAAwB,EAAE,sBAAsB,EAAE,+BAA+B;QACjF,wBAAwB,EAAE,2BAA2B;QACrD,4BAA4B,EAAE,mCAAmC;KAClE,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,CAAC,CAAC,CAAC,CAAC,GAAG,eAAe,EAAE,CAAC;IAC3B,CAAC;IACD,OAAO,CAA+B,CAAC;AACzC,CAAC;AAED,SAAS,SAAS,CAAC,SAAS,GAAyB,EAAE;IACrD,OAAO;QACL,YAAY,EAAE,aAAa,CAAC,GAAG;QAC/B,WAAW,EAAE,aAAa,CAAC,GAAG;QAC9B,YAAY,EAAE,aAAa,CAAC,GAAG;QAC/B,qBAAqB,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE,aAAa,CAAC,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC;QACvG,QAAQ,EAAE,KAAK;QACf,UAAU,EAAE,KAAK;QACjB,SAAS,EAAE,KAAK;QAChB,4BAA4B,EAAE,CAAC;QAC/B,kBAAkB,EAAE,IAAI;QACxB,WAAW,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE;QACtE,gBAAgB,EAAE,KAAK;QACvB,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,SAAS,GAAmC,EAAE;IACjE,OAAO;QACL,UAAU,EAAE,CAAC;QACb,cAAc,EAAE,CAAC;QACjB,YAAY,EAAE,EAAE;QAChB,QAAQ,EAAE,KAAK;QACf,SAAS,EAAE,KAAK;QAChB,SAAS,EAAE,KAAK;QAChB,gBAAgB,EAAE,KAAK;QACvB,kBAAkB,EAAE,KAAK;QACzB,aAAa,EAAE,EAAE;QACjB,cAAc,EAAE,IAAI;QACpB,cAAc,EAAE,IAAI;QACpB,eAAe,EAAE,IAAI;QACrB,kBAAkB,EAAE,IAAI;QACxB,kBAAkB,EAAE,IAAI;QACxB,mBAAmB,EAAE,IAAI;QACzB,mBAAmB,EAAE,KAAK;QAC1B,0BAA0B,EAAE,CAAC;QAC7B,YAAY,EAAE,KAAK;QACnB,GAAG,SAAS;KACuB,CAAC;AACxC,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;AAC1E,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAA+B,CAAC;AACtF,CAAC;AAED,SAAS,UAAU;IACjB,OAAO;QACL,WAAW,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,CAAC,EAAE,EAAE;QAC5C,eAAe,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC;QACtG,aAAa,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,eAAe,EAAE,EAAE,CAAC,EAAE,EAAE;QAChD,mBAAmB,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,qBAAqB,EAAE,EAAE,CAAC,EAAE,EAAE;QAC5D,aAAa,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,eAAe,EAAE,EAAE,CAAC,EAAE,EAAE;QAChD,kBAAkB,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,oBAAoB,EAAE,EAAE,CAAC,EAAE,EAAE;QAC1D,QAAQ,EAAE,EAAE,CAAC,EAAE,EAAE;KACX,CAAC;AACX,CAAC;AAED,SAAS,cAAc;IACrB,OAAO;QACL,uBAAuB,EAAE,EAAE,CAAC,EAAE,EAAE;QAChC,sBAAsB,EAAE,EAAE,CAAC,EAAE,EAAE;QAC/B,wBAAwB,EAAE,EAAE,CAAC,EAAE,EAAE;QACjC,sBAAsB,EAAE,EAAE,CAAC,EAAE,EAAE;QAC/B,wBAAwB,EAAE,EAAE,CAAC,EAAE,EAAE;KAClC,CAAC;AACJ,CAAC;AAED,iFAAiF;AAEjF,QAAQ,CAAC,+BAA+B,EAAE,KAAK,IAAI,EAAE;IACnD,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;IAEtE,IAAI,YAA+C,CAAC;IACpD,IAAI,KAAkB,CAAC;IAEvB,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,kCAAkC,CAAC,CAAC;QAE7E,KAAK,GAAG,SAAS,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,WAAW,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;QAElC,MAAM,MAAM,GAAG,cAAc,EAAS,CAAC;QACvC,YAAY,GAAG,IAAI,YAAY,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,EAAS,EAAE,GAAU,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,UAAU,EAAE,EAAE,MAAM,CAAC,CAAC;IAC7H,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,KAAK,CAAC,YAAY,GAAG,aAAa,CAAC,SAAS,CAAC;QAC7C,MAAM,CAAC,YAAY,CAAC,gBAAgB,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,KAAK,CAAC,YAAY,GAAG,aAAa,CAAC,IAAI,CAAC;QACxC,MAAM,CAAC,YAAY,CAAC,gBAAgB,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,KAAK,CAAC,YAAY,GAAG,aAAa,CAAC,IAAI,CAAC;QACxC,MAAM,CAAC,YAAY,CAAC,gBAAgB,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,gFAAgF;AAEhF,QAAQ,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;IACpD,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;IACtE,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,kCAAkC,CAAC,CAAC;IAE7E,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;QAC5F,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,YAAY,EAAE,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC;QAC/F,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,cAAc,EAAS,CAAC;QACvC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAS,EAAE,GAAU,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAElI,MAAM,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACrF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qFAAqF,EAAE,KAAK,IAAI,EAAE;QACnG,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,YAAY,EAAE,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9G,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,cAAc,EAAS,CAAC;QACvC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAS,EAAE,GAAU,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAElI,MAAM,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACrF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,gBAAgB,EAAE,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,YAAY,EAAE,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/G,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,cAAc,EAAS,CAAC;QACvC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAS,EAAE,GAAU,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAElI,MAAM,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACrF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACpD,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;QAC/C,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,YAAY,EAAE,aAAa,CAAC,GAAG,EAAE,WAAW,EAAE,aAAa,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7F,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,cAAc,EAAS,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAS,EAAE,GAAU,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,UAAU,EAAE,EAAE,MAAM,CAAC,CAAC;QAExI,MAAM,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;QAC3F,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,kFAAkF;AAClF,0EAA0E;AAC1E,0CAA0C;AAE1C,QAAQ,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;IACxE,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;IACtE,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,kCAAkC,CAAC,CAAC;IAE7E,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,YAAY,EAAE,aAAa,CAAC,KAAK,EAAE,CAAC,CAAC;QAC/D,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACpB,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC;QAE3C,MAAM,OAAO,GAAG,IAAI,YAAY,CAC9B,YAAY,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAS,EAC/C,WAAW,EAAS,EAAE,GAAG,EAAE,WAAW,EAAE,EACxC,UAAU,EAAE,EAAE,cAAc,EAAS,CACtC,CAAC;QAEF,OAAO,CAAC,eAAe,CAAC,aAAa,CAAC,SAAS,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;QAEtE,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,YAAY,EAAE,aAAa,CAAC,SAAS,EAAE,CAAC,CAAC;QACnE,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACpB,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC;QAE3C,MAAM,OAAO,GAAG,IAAI,YAAY,CAC9B,YAAY,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAS,EAC/C,WAAW,EAAS,EAAE,GAAG,EAAE,WAAW,EAAE,EACxC,UAAU,EAAE,EAAE,cAAc,EAAS,CACtC,CAAC;QAEF,OAAO,CAAC,eAAe,CAAC,aAAa,CAAC,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;QAElE,MAAM,CAAC,GAAG,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,iFAAiF;AACjF,6EAA6E;AAC7E,4DAA4D;AAE5D,QAAQ,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;IACvE,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;IACtE,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,kCAAkC,CAAC,CAAC;IAE7E,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,YAAY,EAAE,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAChF,MAAM,OAAO,GAAG,IAAI,YAAY,CAC9B,YAAY,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAS,EAC/C,WAAW,EAAS,EAAE,IAAI,eAAe,EAAE,EAAE,WAAW,EAAE,EAC1D,UAAU,EAAE,EAAE,cAAc,EAAS,CACtC,CAAC;QAEF,OAAO,CAAC,iBAAiB,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAEtE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,KAAK,GAAG,SAAS,CAAC;YACtB,YAAY,EAAE,aAAa,CAAC,SAAS;YACrC,WAAW,EAAE,aAAa,CAAC,KAAK;YAChC,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,YAAY,CAC9B,YAAY,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAS,EAC/C,WAAW,EAAS,EAAE,IAAI,eAAe,EAAE,EAAE,WAAW,EAAE,EAC1D,UAAU,EAAE,EAAE,cAAc,EAAS,CACtC,CAAC;QAEF,OAAO,CAAC,iBAAiB,CAAC,aAAa,CAAC,GAAG,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAErE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,iFAAiF;AAEjF,QAAQ,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;IACjD,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;IACtE,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,kCAAkC,CAAC,CAAC;IAE7E,SAAS,WAAW,CAAC,KAAkB;QACrC,OAAO,IAAI,YAAY,CACrB,YAAY,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,EAAE,EAAS,EACzE,WAAW,EAAS,EAAE,IAAI,eAAe,EAAE,EAAE,WAAW,EAAE,EAC1D,UAAU,EAAE,EAAE,cAAc,EAAS,CACtC,CAAC;IACJ,CAAC;IAED,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACnG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACnG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzE,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACnG,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,gFAAgF;AAEhF,QAAQ,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;IAC7E,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;IACtE,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,kCAAkC,CAAC,CAAC;IAE7E,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,YAAY,EAAE,aAAa,CAAC,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,CAAC;QACrF,MAAM,OAAO,GAAG,IAAI,YAAY,CAC9B,YAAY,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAS,EAC/C,WAAW,EAAS,EAAE,IAAI,eAAe,EAAE,EAAE,WAAW,EAAE,EAC1D,UAAU,EAAE,EAAE,cAAc,EAAS,CACtC,CAAC;QAEF,OAAO,CAAC,iBAAiB,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAEtE,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,gFAAgF;AAEhF,QAAQ,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;IACzE,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;IACtE,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,kCAAkC,CAAC,CAAC;IAE7E,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,YAAY,EAAE,aAAa,CAAC,KAAK,EAAE,CAAC,CAAC;QAC/D,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,cAAc,EAAS,CAAC;QAEvC,MAAM,OAAO,GAAG,IAAI,YAAY,CAC9B,YAAY,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAS,EAC/C,WAAW,EAAS,EAAE,GAAG,EAAE,WAAW,EAAE,EACxC,UAAU,EAAE,EAAE,MAAM,CACrB,CAAC;QAEF,OAAO,CAAC,eAAe,CAAC,aAAa,CAAC,SAAS,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;QAEtE,MAAM,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,186 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { SecurityState } from '../types/security-state-type.js';
3
+ import { OriginType } from '../types/origin-type.js';
4
+ import { SwitchHandler } from '../handlers/switch-handler.js';
5
+ // ── Minimal mocks ─────────────────────────────────────────────────────────────
6
+ function makeState(overrides = {}) {
7
+ return {
8
+ currentState: SecurityState.OFF,
9
+ targetState: SecurityState.OFF,
10
+ defaultState: SecurityState.OFF,
11
+ availableTargetStates: [SecurityState.HOME, SecurityState.AWAY, SecurityState.NIGHT, SecurityState.OFF],
12
+ isArming: false,
13
+ isTripping: false,
14
+ isKnocked: false,
15
+ serverAuthenticationAttempts: 0,
16
+ pausedCurrentState: null,
17
+ armingLocks: { global: false, home: false, away: false, night: false },
18
+ modeAwayExtended: false,
19
+ ...overrides,
20
+ };
21
+ }
22
+ function makeOptions(overrides = {}) {
23
+ return {
24
+ armSeconds: 0,
25
+ triggerSeconds: 0,
26
+ resetMinutes: 10,
27
+ testMode: false,
28
+ proxyMode: false,
29
+ saveState: false,
30
+ overrideOff: false,
31
+ doubleKnock: false,
32
+ doubleKnockSeconds: 90,
33
+ doubleKnockModes: [],
34
+ homeTriggerSeconds: null,
35
+ awayTriggerSeconds: null,
36
+ nightTriggerSeconds: null,
37
+ homeDoubleKnockSeconds: null,
38
+ awayDoubleKnockSeconds: null,
39
+ nightDoubleKnockSeconds: null,
40
+ modeAwayExtendedSwitchTriggerSeconds: null,
41
+ armingLockSwitch: false,
42
+ armingLockSwitches: false,
43
+ disabledModes: [],
44
+ homeArmSeconds: null,
45
+ awayArmSeconds: null,
46
+ nightArmSeconds: null,
47
+ trippedMotionSensor: false,
48
+ trippedMotionSensorSeconds: 5,
49
+ resetOffFlow: false,
50
+ ...overrides,
51
+ };
52
+ }
53
+ function makeMockLog() {
54
+ return { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() };
55
+ }
56
+ function makeMockStateHandler() {
57
+ return {
58
+ updateTargetState: vi.fn().mockReturnValue({ success: true }),
59
+ getArmingSeconds: vi.fn().mockReturnValue(0),
60
+ };
61
+ }
62
+ function makeMockTimers() {
63
+ return {
64
+ setArmTimer: vi.fn(), clearArmTimer: vi.fn(),
65
+ setTriggerTimer: vi.fn(), clearTriggerTimer: vi.fn(), isTriggerRunning: vi.fn().mockReturnValue(false),
66
+ setPauseTimer: vi.fn(), clearPauseTimer: vi.fn(),
67
+ setDoubleKnockTimer: vi.fn(), clearDoubleKnockTimer: vi.fn(),
68
+ setResetTimer: vi.fn(), clearResetTimer: vi.fn(),
69
+ setTrippedInterval: vi.fn(), clearTrippedInterval: vi.fn(),
70
+ clearAll: vi.fn(),
71
+ };
72
+ }
73
+ function makeHandler(state, options = makeOptions()) {
74
+ const stateHandler = makeMockStateHandler();
75
+ const log = makeMockLog();
76
+ const timers = makeMockTimers();
77
+ const handler = new SwitchHandler(state, options, log, timers, stateHandler);
78
+ return { handler, stateHandler, log, timers };
79
+ }
80
+ // ── SwitchHandler tests ───────────────────────────────────────────────────────
81
+ describe('SwitchHandler', () => {
82
+ it('setModeSwitch arms via updateTargetState with the arming delay', () => {
83
+ const state = makeState();
84
+ const { handler, stateHandler } = makeHandler(state);
85
+ stateHandler.getArmingSeconds.mockReturnValue(42);
86
+ const result = handler.setModeSwitch(SecurityState.HOME, true);
87
+ expect(stateHandler.updateTargetState).toHaveBeenCalledWith(SecurityState.HOME, OriginType.INTERNAL, 42);
88
+ expect(result.success).toBe(true);
89
+ });
90
+ it('setModeSwitch reports how updateTargetState refused the change', () => {
91
+ const state = makeState();
92
+ const { handler, stateHandler } = makeHandler(state);
93
+ stateHandler.updateTargetState.mockReturnValue({ success: false, reason: 'arming is blocked by an arming lock switch' });
94
+ const result = handler.setModeSwitch(SecurityState.HOME, true);
95
+ expect(result.success).toBe(false);
96
+ expect(result.reason).toBe('arming is blocked by an arming lock switch');
97
+ });
98
+ it('setModeSwitch rejects turning a mode switch off', () => {
99
+ const state = makeState();
100
+ const { handler, stateHandler } = makeHandler(state);
101
+ const result = handler.setModeSwitch(SecurityState.HOME, false);
102
+ expect(result.success).toBe(false);
103
+ expect(stateHandler.updateTargetState).not.toHaveBeenCalled();
104
+ });
105
+ it('setModeOffSwitch disarms and propagates refusals', () => {
106
+ const state = makeState();
107
+ const { handler, stateHandler } = makeHandler(state);
108
+ handler.setModeOffSwitch(true);
109
+ expect(stateHandler.updateTargetState).toHaveBeenCalledWith(SecurityState.OFF, OriginType.INTERNAL, 0);
110
+ stateHandler.updateTargetState.mockReturnValue({ success: false, reason: 'target mode is disabled' });
111
+ const refused = handler.setModeOffSwitch(true);
112
+ expect(refused).toEqual({ success: false, reason: 'target mode is disabled' });
113
+ const rejected = handler.setModeOffSwitch(false);
114
+ expect(rejected.success).toBe(false);
115
+ // Only the two successful ON commands reach updateTargetState.
116
+ expect(stateHandler.updateTargetState).toHaveBeenCalledTimes(2);
117
+ });
118
+ it('setModeAwayExtendedSwitch arms away and records the extended flag', () => {
119
+ const state = makeState();
120
+ const { handler, stateHandler } = makeHandler(state);
121
+ const result = handler.setModeAwayExtendedSwitch(true);
122
+ expect(stateHandler.updateTargetState).toHaveBeenCalledWith(SecurityState.AWAY, OriginType.INTERNAL, 0);
123
+ expect(state.modeAwayExtended).toBe(true);
124
+ expect(result.success).toBe(true);
125
+ });
126
+ it('setModeAwayExtendedSwitch does not set the extended flag when arming was refused', () => {
127
+ const state = makeState();
128
+ const { handler, stateHandler } = makeHandler(state);
129
+ stateHandler.updateTargetState.mockReturnValue({ success: false, reason: 'arming is blocked by an arming lock switch' });
130
+ const result = handler.setModeAwayExtendedSwitch(true);
131
+ expect(result.success).toBe(false);
132
+ expect(state.modeAwayExtended).toBe(false);
133
+ });
134
+ it('setModeAwayExtendedSwitch rejects turning off', () => {
135
+ const state = makeState();
136
+ const { handler } = makeHandler(state);
137
+ expect(handler.setModeAwayExtendedSwitch(false).success).toBe(false);
138
+ });
139
+ it('setModePauseSwitch rejects while triggered', () => {
140
+ const state = makeState({ currentState: SecurityState.TRIGGERED });
141
+ const { handler, timers } = makeHandler(state);
142
+ const result = handler.setModePauseSwitch(true);
143
+ expect(result.success).toBe(false);
144
+ expect(timers.setPauseTimer).not.toHaveBeenCalled();
145
+ });
146
+ it('setModePauseSwitch rejects while disarmed', () => {
147
+ const state = makeState({ currentState: SecurityState.OFF });
148
+ const { handler, timers } = makeHandler(state);
149
+ const result = handler.setModePauseSwitch(true);
150
+ expect(result.success).toBe(false);
151
+ expect(timers.setPauseTimer).not.toHaveBeenCalled();
152
+ });
153
+ it('setModePauseSwitch pauses and schedules the resume timer', () => {
154
+ const state = makeState({ currentState: SecurityState.HOME });
155
+ const { handler, stateHandler, timers } = makeHandler(state, makeOptions({ pauseMinutes: 5 }));
156
+ const result = handler.setModePauseSwitch(true);
157
+ expect(state.pausedCurrentState).toBe(SecurityState.HOME);
158
+ expect(stateHandler.updateTargetState).toHaveBeenCalledWith(SecurityState.OFF, OriginType.INTERNAL, 0);
159
+ expect(timers.setPauseTimer).toHaveBeenCalledWith(5 * 60 * 1000, expect.any(Function));
160
+ expect(result.success).toBe(true);
161
+ });
162
+ it('setModePauseSwitch cancel resumes the paused mode', () => {
163
+ const state = makeState({ currentState: SecurityState.OFF, pausedCurrentState: SecurityState.AWAY });
164
+ const { handler, stateHandler, timers } = makeHandler(state);
165
+ const result = handler.setModePauseSwitch(false);
166
+ expect(timers.clearPauseTimer).toHaveBeenCalled();
167
+ expect(stateHandler.updateTargetState).toHaveBeenCalledWith(SecurityState.AWAY, OriginType.INTERNAL, 0);
168
+ expect(result.success).toBe(true);
169
+ });
170
+ it('updateArmingLock stores the lock on state and returns success', () => {
171
+ const state = makeState();
172
+ const { handler } = makeHandler(state);
173
+ expect(handler.updateArmingLock('global', true).success).toBe(true);
174
+ expect(state.armingLocks.global).toBe(true);
175
+ expect(handler.updateArmingLock('away', false).success).toBe(true);
176
+ expect(state.armingLocks.away).toBe(false);
177
+ });
178
+ it('updateArmingLock rejects unknown modes', () => {
179
+ const state = makeState();
180
+ const { handler } = makeHandler(state);
181
+ const result = handler.updateArmingLock('garage', true);
182
+ expect(result.success).toBe(false);
183
+ expect(result.reason).toBe('unknown arming lock mode: garage');
184
+ });
185
+ });
186
+ //# sourceMappingURL=switch-handler.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"switch-handler.test.js","sourceRoot":"","sources":["../../src/tests/switch-handler.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};