homebridge-securitysystem 0.0.3-matter → 0.0.4-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.
@@ -23,7 +23,9 @@ export declare class SecuritySystem {
23
23
  /**
24
24
  * Replaces any existing services (from a restored cached accessory) with fresh
25
25
  * instances and adds all exposed services exactly once. addService() throws if
26
- * the accessory already carries a service with the same UUID without subtype.
26
+ * the accessory already carries a service with the same UUID without subtype,
27
+ * so stale services are matched by UUID and removed beforehand (getService()
28
+ * matches display names, not UUIDs, and is therefore useless here).
27
29
  */
28
30
  private populateAccessory;
29
31
  private buildState;
@@ -125,13 +125,14 @@ export class SecuritySystem {
125
125
  /**
126
126
  * Replaces any existing services (from a restored cached accessory) with fresh
127
127
  * instances and adds all exposed services exactly once. addService() throws if
128
- * the accessory already carries a service with the same UUID without subtype.
128
+ * the accessory already carries a service with the same UUID without subtype,
129
+ * so stale services are matched by UUID and removed beforehand (getService()
130
+ * matches display names, not UUIDs, and is therefore useless here).
129
131
  */
130
132
  populateAccessory() {
131
133
  this.accessory.updateDisplayName(this.options.name);
132
134
  for (const service of this.serviceList) {
133
- const existingService = this.accessory.getService(service.UUID);
134
- if (existingService) {
135
+ for (const existingService of this.accessory.services.filter(existing => existing.UUID === service.UUID)) {
135
136
  this.accessory.removeService(existingService);
136
137
  }
137
138
  this.accessory.addService(service);
@@ -1 +1 @@
1
- {"version":3,"file":"security-system.js","sourceRoot":"","sources":["../src/security-system.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"security-system.js","sourceRoot":"","sources":["../src/security-system.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,194 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { SecuritySystem } from '../security-system.js';
3
+ const HAP_UUID_BASE = '-0000-1000-8000-0026BB765291';
4
+ function hapUuid(suffix) {
5
+ return `000000${suffix}${HAP_UUID_BASE}`;
6
+ }
7
+ const SECURITY_SYSTEM_SERVICE_UUID = hapUuid('7E');
8
+ const ACCESSORY_INFORMATION_SERVICE_UUID = hapUuid('3E');
9
+ class FakeCharacteristic {
10
+ value;
11
+ setProps() {
12
+ return this;
13
+ }
14
+ onGet() {
15
+ return this;
16
+ }
17
+ onSet() {
18
+ return this;
19
+ }
20
+ on() {
21
+ return this;
22
+ }
23
+ updateCharacteristic() {
24
+ return this;
25
+ }
26
+ }
27
+ class FakeService {
28
+ UUID;
29
+ displayName;
30
+ name;
31
+ subtype;
32
+ characteristicByUuid = new Map();
33
+ constructor(uuid, displayName = '') {
34
+ this.UUID = uuid;
35
+ this.displayName = displayName;
36
+ this.name = displayName;
37
+ }
38
+ addCharacteristic(spec) {
39
+ this.characteristic(spec);
40
+ return this;
41
+ }
42
+ getCharacteristic(spec) {
43
+ return this.characteristic(spec);
44
+ }
45
+ setCharacteristic(spec, value) {
46
+ this.characteristic(spec).value = value;
47
+ return this;
48
+ }
49
+ updateCharacteristic(spec, value) {
50
+ this.characteristic(spec).value = value;
51
+ }
52
+ removeAllListeners() {
53
+ // no-op, mirrors the cleanup performed on removal
54
+ }
55
+ characteristic(spec) {
56
+ let characteristic = this.characteristicByUuid.get(spec.UUID);
57
+ if (!characteristic) {
58
+ characteristic = new FakeCharacteristic();
59
+ this.characteristicByUuid.set(spec.UUID, characteristic);
60
+ }
61
+ return characteristic;
62
+ }
63
+ }
64
+ const Characteristic = {
65
+ SecuritySystemCurrentState: { UUID: hapUuid('6F') },
66
+ SecuritySystemTargetState: { UUID: hapUuid('70') },
67
+ ConfiguredName: { UUID: hapUuid('E3') },
68
+ Name: { UUID: hapUuid('23') },
69
+ Identify: { UUID: hapUuid('14') },
70
+ Manufacturer: { UUID: hapUuid('20') },
71
+ Model: { UUID: hapUuid('21') },
72
+ SerialNumber: { UUID: hapUuid('30') },
73
+ };
74
+ const Service = {
75
+ SecuritySystem: class extends FakeService {
76
+ constructor(displayName = '') {
77
+ super(SECURITY_SYSTEM_SERVICE_UUID, displayName);
78
+ }
79
+ },
80
+ AccessoryInformation: class extends FakeService {
81
+ constructor() {
82
+ super(ACCESSORY_INFORMATION_SERVICE_UUID);
83
+ }
84
+ },
85
+ };
86
+ function makeAccessory(initialServices = []) {
87
+ const services = [...initialServices];
88
+ return {
89
+ services,
90
+ updateDisplayName: vi.fn(),
91
+ on: vi.fn(),
92
+ addService: (service) => {
93
+ for (const existing of services) {
94
+ if (existing.UUID !== service.UUID) {
95
+ continue;
96
+ }
97
+ if (!service.subtype) {
98
+ throw new Error(`Cannot add a Service with the same UUID '${existing.UUID}' as another Service in this Accessory without also defining a unique 'subtype' property.`);
99
+ }
100
+ if (service.subtype === existing.subtype) {
101
+ throw new Error(`Cannot add a Service with the same UUID '${existing.UUID}' and subtype '${existing.subtype}' as another Service in this Accessory.`);
102
+ }
103
+ }
104
+ services.push(service);
105
+ return service;
106
+ },
107
+ removeService: (service) => {
108
+ const index = services.indexOf(service);
109
+ if (index >= 0) {
110
+ services.splice(index, 1);
111
+ }
112
+ },
113
+ // Mirrors hap-nodejs: a string argument matches displayName/name/subtype,
114
+ // so a full UUID string never finds a service here.
115
+ getService: (name) => {
116
+ for (const service of services) {
117
+ if (typeof name === 'string') {
118
+ if (service.displayName === name || service.name === name || service.subtype === name) {
119
+ return service;
120
+ }
121
+ }
122
+ else if (name.UUID === service.UUID) {
123
+ return service;
124
+ }
125
+ }
126
+ return undefined;
127
+ },
128
+ };
129
+ }
130
+ function makeApi() {
131
+ return {
132
+ hap: { Service, Characteristic },
133
+ user: { storagePath: () => '/tmp/homebridge-securitysystem-test' },
134
+ on: vi.fn(),
135
+ };
136
+ }
137
+ function makeLog() {
138
+ return {
139
+ info: vi.fn(),
140
+ warn: vi.fn(),
141
+ error: vi.fn(),
142
+ debug: vi.fn(),
143
+ };
144
+ }
145
+ function makeConfig() {
146
+ return {
147
+ platform: 'security-system',
148
+ name: 'Security System',
149
+ serial_number: 'SEC-1',
150
+ default_mode: 'Off',
151
+ arm_seconds: 0,
152
+ trigger_seconds: 0,
153
+ reset_minutes: 10,
154
+ };
155
+ }
156
+ function instantiate(accessory) {
157
+ return new SecuritySystem(makeLog(), makeConfig(), makeApi(), accessory);
158
+ }
159
+ // ── Tests ─────────────────────────────────────────────────────────────────────
160
+ describe('SecuritySystem accessory population', () => {
161
+ it('exposes exactly one security system service and one accessory information service', () => {
162
+ const accessory = makeAccessory();
163
+ instantiate(accessory);
164
+ expect(accessory.services.filter(service => service.UUID === SECURITY_SYSTEM_SERVICE_UUID)).toHaveLength(1);
165
+ expect(accessory.services.filter(service => service.UUID === ACCESSORY_INFORMATION_SERVICE_UUID)).toHaveLength(1);
166
+ expect(accessory.services).toHaveLength(2);
167
+ });
168
+ it('replaces stale services on a restored cached accessory instead of throwing on duplicate UUIDs', () => {
169
+ const staleSecuritySystemService = new Service.SecuritySystem('Security System');
170
+ const staleAccessoryInformationService = new Service.AccessoryInformation();
171
+ const accessory = makeAccessory([staleSecuritySystemService, staleAccessoryInformationService]);
172
+ // Must not throw "Cannot add a Service with the same UUID".
173
+ instantiate(accessory);
174
+ const securitySystemServices = accessory.services
175
+ .filter(service => service.UUID === SECURITY_SYSTEM_SERVICE_UUID);
176
+ expect(securitySystemServices).toHaveLength(1);
177
+ expect(securitySystemServices[0]).not.toBe(staleSecuritySystemService);
178
+ const accessoryInformationServices = accessory.services
179
+ .filter(service => service.UUID === ACCESSORY_INFORMATION_SERVICE_UUID);
180
+ expect(accessoryInformationServices).toHaveLength(1);
181
+ expect(accessoryInformationServices[0]).not.toBe(staleAccessoryInformationService);
182
+ });
183
+ it('keeps the fresh service instances when the cached accessory already holds several stale copies', () => {
184
+ const accessory = makeAccessory([
185
+ new Service.SecuritySystem('Security System'),
186
+ new Service.AccessoryInformation(),
187
+ new Service.SecuritySystem('Security System'),
188
+ ]);
189
+ instantiate(accessory);
190
+ expect(accessory.services.filter(service => service.UUID === SECURITY_SYSTEM_SERVICE_UUID)).toHaveLength(1);
191
+ expect(accessory.services.filter(service => service.UUID === ACCESSORY_INFORMATION_SERVICE_UUID)).toHaveLength(1);
192
+ });
193
+ });
194
+ //# sourceMappingURL=security-system.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-system.test.js","sourceRoot":"","sources":["../../src/tests/security-system.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "homebridge-securitysystem",
3
3
  "displayName": "Homebridge Security System",
4
4
  "type": "module",
5
- "version": "0.0.3-matter",
5
+ "version": "0.0.4-matter",
6
6
  "description": "Homebridge plugin that creates a security system accessory that can be triggered by HomeKit sensors.",
7
7
  "author": "MiguelRipoll23",
8
8
  "license": "MIT",
@@ -153,13 +153,14 @@ export class SecuritySystem {
153
153
  /**
154
154
  * Replaces any existing services (from a restored cached accessory) with fresh
155
155
  * instances and adds all exposed services exactly once. addService() throws if
156
- * the accessory already carries a service with the same UUID without subtype.
156
+ * the accessory already carries a service with the same UUID without subtype,
157
+ * so stale services are matched by UUID and removed beforehand (getService()
158
+ * matches display names, not UUIDs, and is therefore useless here).
157
159
  */
158
160
  private populateAccessory(): void {
159
161
  this.accessory.updateDisplayName(this.options.name);
160
162
  for (const service of this.serviceList) {
161
- const existingService = this.accessory.getService(service.UUID);
162
- if (existingService) {
163
+ for (const existingService of this.accessory.services.filter(existing => existing.UUID === service.UUID)) {
163
164
  this.accessory.removeService(existingService);
164
165
  }
165
166
  this.accessory.addService(service);
@@ -0,0 +1,245 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import type { API, Logging, PlatformAccessory } from 'homebridge';
3
+ import { SecuritySystem } from '../security-system.js';
4
+
5
+ const HAP_UUID_BASE = '-0000-1000-8000-0026BB765291';
6
+
7
+ function hapUuid(suffix: string): string {
8
+ return `000000${suffix}${HAP_UUID_BASE}`;
9
+ }
10
+
11
+ const SECURITY_SYSTEM_SERVICE_UUID = hapUuid('7E');
12
+ const ACCESSORY_INFORMATION_SERVICE_UUID = hapUuid('3E');
13
+
14
+ // ── Homebridge mocks ─────────────────────────────────────────────────────────
15
+ //
16
+ // The fake service/accessory objects intentionally mirror hap-nodejs semantics:
17
+ // addService() throws on a duplicate UUID without subtype, and getService()
18
+ // matches display names/names/subtypes for string arguments — never UUIDs.
19
+
20
+ type CharacteristicSpec = { UUID: string };
21
+
22
+ class FakeCharacteristic {
23
+ value: unknown;
24
+
25
+ setProps(): this {
26
+ return this;
27
+ }
28
+
29
+ onGet(): this {
30
+ return this;
31
+ }
32
+
33
+ onSet(): this {
34
+ return this;
35
+ }
36
+
37
+ on(): this {
38
+ return this;
39
+ }
40
+
41
+ updateCharacteristic(): this {
42
+ return this;
43
+ }
44
+ }
45
+
46
+ class FakeService {
47
+ readonly UUID: string;
48
+ displayName: string;
49
+ name: string;
50
+ subtype: string | undefined;
51
+ private readonly characteristicByUuid = new Map<string, FakeCharacteristic>();
52
+
53
+ constructor(uuid: string, displayName = '') {
54
+ this.UUID = uuid;
55
+ this.displayName = displayName;
56
+ this.name = displayName;
57
+ }
58
+
59
+ addCharacteristic(spec: CharacteristicSpec): this {
60
+ this.characteristic(spec);
61
+ return this;
62
+ }
63
+
64
+ getCharacteristic(spec: CharacteristicSpec): FakeCharacteristic {
65
+ return this.characteristic(spec);
66
+ }
67
+
68
+ setCharacteristic(spec: CharacteristicSpec, value: unknown): this {
69
+ this.characteristic(spec).value = value;
70
+ return this;
71
+ }
72
+
73
+ updateCharacteristic(spec: CharacteristicSpec, value: unknown): void {
74
+ this.characteristic(spec).value = value;
75
+ }
76
+
77
+ removeAllListeners(): void {
78
+ // no-op, mirrors the cleanup performed on removal
79
+ }
80
+
81
+ private characteristic(spec: CharacteristicSpec): FakeCharacteristic {
82
+ let characteristic = this.characteristicByUuid.get(spec.UUID);
83
+ if (!characteristic) {
84
+ characteristic = new FakeCharacteristic();
85
+ this.characteristicByUuid.set(spec.UUID, characteristic);
86
+ }
87
+ return characteristic;
88
+ }
89
+ }
90
+
91
+ const Characteristic = {
92
+ SecuritySystemCurrentState: { UUID: hapUuid('6F') },
93
+ SecuritySystemTargetState: { UUID: hapUuid('70') },
94
+ ConfiguredName: { UUID: hapUuid('E3') },
95
+ Name: { UUID: hapUuid('23') },
96
+ Identify: { UUID: hapUuid('14') },
97
+ Manufacturer: { UUID: hapUuid('20') },
98
+ Model: { UUID: hapUuid('21') },
99
+ SerialNumber: { UUID: hapUuid('30') },
100
+ };
101
+
102
+ const Service = {
103
+ SecuritySystem: class extends FakeService {
104
+ constructor(displayName = '') {
105
+ super(SECURITY_SYSTEM_SERVICE_UUID, displayName);
106
+ }
107
+ },
108
+ AccessoryInformation: class extends FakeService {
109
+ constructor() {
110
+ super(ACCESSORY_INFORMATION_SERVICE_UUID);
111
+ }
112
+ },
113
+ };
114
+
115
+ function makeAccessory(initialServices: FakeService[] = []) {
116
+ const services: FakeService[] = [...initialServices];
117
+ return {
118
+ services,
119
+ updateDisplayName: vi.fn(),
120
+ on: vi.fn(),
121
+ addService: (service: FakeService) => {
122
+ for (const existing of services) {
123
+ if (existing.UUID !== service.UUID) {
124
+ continue;
125
+ }
126
+ if (!service.subtype) {
127
+ throw new Error(
128
+ `Cannot add a Service with the same UUID '${existing.UUID}' as another Service in this Accessory without also defining a unique 'subtype' property.`,
129
+ );
130
+ }
131
+ if (service.subtype === existing.subtype) {
132
+ throw new Error(
133
+ `Cannot add a Service with the same UUID '${existing.UUID}' and subtype '${existing.subtype}' as another Service in this Accessory.`,
134
+ );
135
+ }
136
+ }
137
+ services.push(service);
138
+ return service;
139
+ },
140
+ removeService: (service: FakeService) => {
141
+ const index = services.indexOf(service);
142
+ if (index >= 0) {
143
+ services.splice(index, 1);
144
+ }
145
+ },
146
+ // Mirrors hap-nodejs: a string argument matches displayName/name/subtype,
147
+ // so a full UUID string never finds a service here.
148
+ getService: (name: string | CharacteristicSpec) => {
149
+ for (const service of services) {
150
+ if (typeof name === 'string') {
151
+ if (service.displayName === name || service.name === name || service.subtype === name) {
152
+ return service;
153
+ }
154
+ } else if (name.UUID === service.UUID) {
155
+ return service;
156
+ }
157
+ }
158
+ return undefined;
159
+ },
160
+ };
161
+ }
162
+
163
+ function makeApi(): API {
164
+ return {
165
+ hap: { Service, Characteristic },
166
+ user: { storagePath: () => '/tmp/homebridge-securitysystem-test' },
167
+ on: vi.fn(),
168
+ } as unknown as API;
169
+ }
170
+
171
+ function makeLog(): Logging {
172
+ return {
173
+ info: vi.fn(),
174
+ warn: vi.fn(),
175
+ error: vi.fn(),
176
+ debug: vi.fn(),
177
+ } as unknown as Logging;
178
+ }
179
+
180
+ function makeConfig(): Record<string, unknown> {
181
+ return {
182
+ platform: 'security-system',
183
+ name: 'Security System',
184
+ serial_number: 'SEC-1',
185
+ default_mode: 'Off',
186
+ arm_seconds: 0,
187
+ trigger_seconds: 0,
188
+ reset_minutes: 10,
189
+ };
190
+ }
191
+
192
+ function instantiate(accessory: ReturnType<typeof makeAccessory>): SecuritySystem {
193
+ return new SecuritySystem(
194
+ makeLog(),
195
+ makeConfig(),
196
+ makeApi(),
197
+ accessory as unknown as PlatformAccessory,
198
+ );
199
+ }
200
+
201
+ // ── Tests ─────────────────────────────────────────────────────────────────────
202
+
203
+ describe('SecuritySystem accessory population', () => {
204
+ it('exposes exactly one security system service and one accessory information service', () => {
205
+ const accessory = makeAccessory();
206
+
207
+ instantiate(accessory);
208
+
209
+ expect(accessory.services.filter(service => service.UUID === SECURITY_SYSTEM_SERVICE_UUID)).toHaveLength(1);
210
+ expect(accessory.services.filter(service => service.UUID === ACCESSORY_INFORMATION_SERVICE_UUID)).toHaveLength(1);
211
+ expect(accessory.services).toHaveLength(2);
212
+ });
213
+
214
+ it('replaces stale services on a restored cached accessory instead of throwing on duplicate UUIDs', () => {
215
+ const staleSecuritySystemService = new Service.SecuritySystem('Security System');
216
+ const staleAccessoryInformationService = new Service.AccessoryInformation();
217
+ const accessory = makeAccessory([staleSecuritySystemService, staleAccessoryInformationService]);
218
+
219
+ // Must not throw "Cannot add a Service with the same UUID".
220
+ instantiate(accessory);
221
+
222
+ const securitySystemServices = accessory.services
223
+ .filter(service => service.UUID === SECURITY_SYSTEM_SERVICE_UUID);
224
+ expect(securitySystemServices).toHaveLength(1);
225
+ expect(securitySystemServices[0]).not.toBe(staleSecuritySystemService);
226
+
227
+ const accessoryInformationServices = accessory.services
228
+ .filter(service => service.UUID === ACCESSORY_INFORMATION_SERVICE_UUID);
229
+ expect(accessoryInformationServices).toHaveLength(1);
230
+ expect(accessoryInformationServices[0]).not.toBe(staleAccessoryInformationService);
231
+ });
232
+
233
+ it('keeps the fresh service instances when the cached accessory already holds several stale copies', () => {
234
+ const accessory = makeAccessory([
235
+ new Service.SecuritySystem('Security System'),
236
+ new Service.AccessoryInformation(),
237
+ new Service.SecuritySystem('Security System'),
238
+ ]);
239
+
240
+ instantiate(accessory);
241
+
242
+ expect(accessory.services.filter(service => service.UUID === SECURITY_SYSTEM_SERVICE_UUID)).toHaveLength(1);
243
+ expect(accessory.services.filter(service => service.UUID === ACCESSORY_INFORMATION_SERVICE_UUID)).toHaveLength(1);
244
+ });
245
+ });