homebridge-verisure 2.0.1 → 3.0.0

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 (68) hide show
  1. package/README.md +33 -2
  2. package/config.schema.json +30 -0
  3. package/dist/accessories/alarm.d.ts +28 -0
  4. package/dist/accessories/alarm.js +129 -0
  5. package/dist/accessories/alarm.js.map +1 -0
  6. package/dist/accessories/base.d.ts +39 -0
  7. package/dist/accessories/base.js +63 -0
  8. package/dist/accessories/base.js.map +1 -0
  9. package/dist/accessories/climateSensor.d.ts +14 -0
  10. package/dist/accessories/climateSensor.js +60 -0
  11. package/dist/accessories/climateSensor.js.map +1 -0
  12. package/dist/accessories/contactSensor.d.ts +14 -0
  13. package/dist/accessories/contactSensor.js +39 -0
  14. package/dist/accessories/contactSensor.js.map +1 -0
  15. package/dist/accessories/doorLock.d.ts +32 -0
  16. package/dist/accessories/doorLock.js +162 -0
  17. package/dist/accessories/doorLock.js.map +1 -0
  18. package/dist/accessories/smartPlug.d.ts +15 -0
  19. package/dist/accessories/smartPlug.js +54 -0
  20. package/dist/accessories/smartPlug.js.map +1 -0
  21. package/dist/bin/verisure.d.ts +2 -0
  22. package/dist/bin/verisure.js +56 -0
  23. package/dist/bin/verisure.js.map +1 -0
  24. package/dist/cache.d.ts +14 -0
  25. package/dist/cache.js +35 -0
  26. package/dist/cache.js.map +1 -0
  27. package/dist/deviceNames.d.ts +3 -0
  28. package/dist/deviceNames.js +16 -0
  29. package/dist/deviceNames.js.map +1 -0
  30. package/dist/i18n/index.d.ts +1 -0
  31. package/dist/i18n/index.js +18 -0
  32. package/dist/i18n/index.js.map +1 -0
  33. package/dist/i18n/nb_NO.json +22 -0
  34. package/dist/i18n/sv_SE.json +22 -0
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.js +7 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/operations.d.ts +27 -0
  39. package/dist/operations.js +322 -0
  40. package/dist/operations.js.map +1 -0
  41. package/dist/overviewPoller.d.ts +53 -0
  42. package/dist/overviewPoller.js +169 -0
  43. package/dist/overviewPoller.js.map +1 -0
  44. package/dist/platform.d.ts +27 -0
  45. package/dist/platform.js +272 -0
  46. package/dist/platform.js.map +1 -0
  47. package/dist/settings.d.ts +21 -0
  48. package/dist/settings.js +25 -0
  49. package/dist/settings.js.map +1 -0
  50. package/dist/types.d.ts +117 -0
  51. package/dist/types.js +12 -0
  52. package/dist/types.js.map +1 -0
  53. package/package.json +39 -16
  54. package/CHANGELOG.md +0 -174
  55. package/bin/verisure +0 -55
  56. package/index.js +0 -6
  57. package/lib/accessories/alarm.js +0 -111
  58. package/lib/accessories/climatesensor.js +0 -68
  59. package/lib/accessories/contactsensor.js +0 -45
  60. package/lib/accessories/doorlock.js +0 -216
  61. package/lib/accessories/index.js +0 -13
  62. package/lib/accessories/smartplug.js +0 -60
  63. package/lib/accessories/verisure.js +0 -62
  64. package/lib/i18n/index.js +0 -11
  65. package/lib/i18n/nb_NO.json +0 -22
  66. package/lib/i18n/sv_SE.json +0 -22
  67. package/lib/operations.js +0 -225
  68. package/lib/platform.js +0 -156
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  [![GitHub Actions badge](https://github.com/ptz0n/homebridge-verisure/workflows/Test/badge.svg)](https://github.com/ptz0n/homebridge-verisure/actions?query=workflow%3ATest)
8
8
 
9
- This is a plugin for [Homebridge](https://github.com/nfarina/homebridge). It's
9
+ This is a plugin for [Homebridge](https://homebridge.io). It's
10
10
  an implementation for your Verisure installation and exposes the following devices:
11
11
 
12
12
  - Security alarm
@@ -19,6 +19,11 @@ an implementation for your Verisure installation and exposes the following devic
19
19
  - Smoke detector
20
20
  - Vibration detector
21
21
 
22
+ ## Requirements
23
+
24
+ * Node.js 22 or later
25
+ * Homebridge 2.0 or later
26
+
22
27
  ## Protect linked devices & accounts
23
28
 
24
29
  If configured, this plugin will expose your security system and door lock.
@@ -74,6 +79,18 @@ your array (list) of enabled platform plugins. Example config:
74
79
  * `showAudioSwitch` Optional boolean for exposing door audio volume switch. Defaults to `true`.
75
80
  * `audioOffValue` Optional string for door audio volume, `SILENCE` or `LOW`. Defaults to `SILENCE`.
76
81
  * `audioOnValue` Optional string for door audio volume, `LOW` or `HIGH`. Defaults to `LOW`.
82
+ * `forceArm` Optional boolean. Bypasses Verisure's "a door/window is open" guard when arming. Defaults to `true` - without it, arming can silently fail depending on sensor state.
83
+ * `excludedAccessoryTypes` Optional array to hide entire accessory types: `alarm`, `climateSensor`, `contactSensor`, `doorLock`, `smartPlug`.
84
+ * `excludedDevices` Optional array of device labels or area names (e.g. `"Kitchen"`) to hide regardless of type.
85
+
86
+ ### Rate limits
87
+
88
+ Verisure limits how many API calls an account can make per day; exceeding it
89
+ gets your account temporarily blocked. This plugin makes one request per
90
+ installation per `pollInterval` tick, shared by every accessory on it, plus a
91
+ short-lived cache that collapses bursts of near-simultaneous HomeKit reads.
92
+ If Verisure does rate-limit the account anyway, polling for that installation
93
+ pauses automatically for 15 minutes.
77
94
 
78
95
  ### Multi-factor authentication
79
96
 
@@ -82,7 +99,7 @@ your array (list) of enabled platform plugins. Example config:
82
99
  In 2021 Verisure started enrolling MFA which requires you to obtain cookies. These cookies are used instead of a `password` in your config and will need to be renewed yearly. After installing the plugin, run `npx homebridge-verisure` in your terminal and copy the output values into your config.
83
100
 
84
101
  ```bash
85
- $ npx homebridge-verisure
102
+ $ npx homebridge-verisure@latest
86
103
  ✔ What is your login email? · foo@bar.com
87
104
  ✔ What is your password? · ********************
88
105
 
@@ -113,3 +130,17 @@ For convenience, the following environment variables can be used instead of plac
113
130
  * `VERISURE_DOOR_CODE`
114
131
  * `VERISURE_EMAIL`
115
132
  * `VERISURE_PASSWORD`
133
+
134
+ ## Known limitations
135
+
136
+ * Verisure's alarm system has no equivalent of HomeKit's "Night" state; a client that lets you pick it anyway (e.g. Eve) will have the request rejected.
137
+ * Climate sensor readings update roughly every 6 hours on Verisure's side; HomeKit will show that same stale-looking value in between.
138
+
139
+ ## Development
140
+
141
+ ```bash
142
+ npm install
143
+ npm run build # compile TypeScript to dist/
144
+ npm test # lint, build, and run the test suite
145
+ npm start # build and launch a local Homebridge instance for manual testing
146
+ ```
@@ -81,6 +81,36 @@
81
81
  ],
82
82
  "required": true,
83
83
  "functionBody": "return model.showAudioSwitch === true;"
84
+ },
85
+ "forceArm": {
86
+ "title": "Force arm",
87
+ "description": "Arm even if a door/window sensor is open. Turning this off can cause arming to silently fail if a sensor isn't in its expected state.",
88
+ "type": "boolean",
89
+ "default": true
90
+ },
91
+ "excludedAccessoryTypes": {
92
+ "title": "Excluded accessory types",
93
+ "type": "array",
94
+ "items": {
95
+ "type": "string",
96
+ "oneOf": [
97
+ { "title": "Alarm", "enum": ["alarm"] },
98
+ { "title": "Climate sensor", "enum": ["climateSensor"] },
99
+ { "title": "Contact sensor", "enum": ["contactSensor"] },
100
+ { "title": "Door lock", "enum": ["doorLock"] },
101
+ { "title": "Smart plug", "enum": ["smartPlug"] }
102
+ ]
103
+ },
104
+ "required": false
105
+ },
106
+ "excludedDevices": {
107
+ "title": "Excluded devices",
108
+ "description": "Device labels or area names to never expose, regardless of type.",
109
+ "type": "array",
110
+ "items": {
111
+ "type": "string"
112
+ },
113
+ "required": false
84
114
  }
85
115
  }
86
116
  },
@@ -0,0 +1,28 @@
1
+ import type { PlatformAccessory } from 'homebridge';
2
+ import type { VerisureInstallation } from 'verisure';
3
+ import type { VerisurePlatform } from '../platform';
4
+ import type { Overview } from '../types';
5
+ import { VerisureAccessoryHandler } from './base';
6
+ import type { AccessoryContext } from './base';
7
+ export declare class Alarm extends VerisureAccessoryHandler {
8
+ private readonly service;
9
+ private readonly alarmCode;
10
+ private readonly forceArm;
11
+ /** Verisure reports transitional statuses (e.g. `CHANGE_IN_PROGRESS`
12
+ * while an arm/disarm is mid-flight) that have no HomeKit equivalent.
13
+ * Falling back to the last resolved state avoids surfacing a read error
14
+ * for what is a normal, brief condition, and is more robust than
15
+ * enumerating every transitional status Verisure might ever send. */
16
+ private lastKnownState?;
17
+ constructor(platform: VerisurePlatform, accessory: PlatformAccessory<AccessoryContext>, installation: VerisureInstallation);
18
+ private armStateMap;
19
+ private toHomeKitState;
20
+ /** Resolves the current HomeKit state for a Verisure status, remembering
21
+ * it for next time. Falls back to the last known state - rather than
22
+ * erroring - for a transitional or otherwise unrecognized status. */
23
+ private resolveCurrentState;
24
+ private toVerisureState;
25
+ private getCurrentAlarmState;
26
+ private setTargetAlarmState;
27
+ protected onOverview(overview: Overview): void;
28
+ }
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Alarm = void 0;
4
+ const operations_1 = require("../operations");
5
+ const base_1 = require("./base");
6
+ class Alarm extends base_1.VerisureAccessoryHandler {
7
+ service;
8
+ alarmCode;
9
+ forceArm;
10
+ /** Verisure reports transitional statuses (e.g. `CHANGE_IN_PROGRESS`
11
+ * while an arm/disarm is mid-flight) that have no HomeKit equivalent.
12
+ * Falling back to the last resolved state avoids surfacing a read error
13
+ * for what is a normal, brief condition, and is more robust than
14
+ * enumerating every transitional status Verisure might ever send. */
15
+ lastKnownState;
16
+ constructor(platform, accessory, installation) {
17
+ super(platform, accessory, installation);
18
+ this.alarmCode = String(this.platformConfig.alarmCode);
19
+ this.forceArm = this.platformConfig.forceArm !== false;
20
+ const { Service, Characteristic } = this.hap;
21
+ this.accessoryInformation.setCharacteristic(Characteristic.Model, 'ALARM');
22
+ this.service = this.accessory.getService(Service.SecuritySystem)
23
+ ?? this.accessory.addService(Service.SecuritySystem, accessory.displayName);
24
+ this.service.getCharacteristic(Characteristic.SecuritySystemCurrentState)
25
+ .onGet(() => this.getCurrentAlarmState());
26
+ const targetState = this.service.getCharacteristic(Characteristic.SecuritySystemTargetState)
27
+ .onGet(() => this.getCurrentAlarmState())
28
+ .onSet((value) => this.setTargetAlarmState(value));
29
+ // There is no "Night" mode in this alarm system; some HomeKit clients
30
+ // (e.g. Eve) let a user pick it anyway despite it being filtered out of
31
+ // validValues here, which used to crash the plugin outright (#85) -
32
+ // setTargetAlarmState() below now rejects it with a proper HAP error
33
+ // instead of throwing synchronously.
34
+ const { NIGHT_ARM } = Characteristic.SecuritySystemTargetState;
35
+ targetState.setProps({
36
+ validValues: targetState.props.validValues?.filter((state) => state !== NIGHT_ARM),
37
+ });
38
+ }
39
+ armStateMap() {
40
+ const { SecuritySystemCurrentState } = this.hap.Characteristic;
41
+ return {
42
+ ARMED_AWAY: SecuritySystemCurrentState.AWAY_ARM,
43
+ ARMED_HOME: SecuritySystemCurrentState.STAY_ARM,
44
+ DISARMED: SecuritySystemCurrentState.DISARMED,
45
+ };
46
+ }
47
+ toHomeKitState(statusType) {
48
+ const value = this.armStateMap()[statusType];
49
+ if (value === undefined) {
50
+ throw new Error(`Cannot resolve arm state from unknown Verisure status: ${statusType}`);
51
+ }
52
+ return value;
53
+ }
54
+ /** Resolves the current HomeKit state for a Verisure status, remembering
55
+ * it for next time. Falls back to the last known state - rather than
56
+ * erroring - for a transitional or otherwise unrecognized status. */
57
+ resolveCurrentState(statusType) {
58
+ try {
59
+ this.lastKnownState = this.toHomeKitState(statusType);
60
+ }
61
+ catch (error) {
62
+ if (this.lastKnownState === undefined) {
63
+ throw error;
64
+ }
65
+ this.logPrefixed(`${error.message} - reporting the last known state instead.`, 'debug');
66
+ }
67
+ return this.lastKnownState;
68
+ }
69
+ toVerisureState(value) {
70
+ const map = this.armStateMap();
71
+ const match = Object.keys(map).find((key) => map[key] === value);
72
+ if (!match) {
73
+ throw new this.hap.HapStatusError(-70410 /* this.hap.HAPStatus.INVALID_VALUE_IN_REQUEST */);
74
+ }
75
+ return match;
76
+ }
77
+ async getCurrentAlarmState() {
78
+ this.logPrefixed('Getting current alarm state.', 'debug');
79
+ const overview = await this.platform.poller(this.installation).getOverview();
80
+ if (!overview.armState) {
81
+ this.logPrefixed(`No armState in overview; last known state: ${this.lastKnownState}`, 'debug');
82
+ if (this.lastKnownState !== undefined) {
83
+ return this.lastKnownState;
84
+ }
85
+ throw new this.hap.HapStatusError(-70402 /* this.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
86
+ }
87
+ try {
88
+ const value = this.resolveCurrentState(overview.armState.statusType);
89
+ this.logPrefixed(`Resolved current alarm state: ${value} (raw Verisure status: ${overview.armState.statusType})`, 'debug');
90
+ return value;
91
+ }
92
+ catch {
93
+ throw new this.hap.HapStatusError(-70402 /* this.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
94
+ }
95
+ }
96
+ async setTargetAlarmState(value) {
97
+ this.logPrefixed(`Setting target alarm state to: ${value}`);
98
+ const targetArmState = this.toVerisureState(value);
99
+ const operation = {
100
+ ARMED_AWAY: () => (0, operations_1.armAwayOperation)(this.alarmCode, this.forceArm),
101
+ ARMED_HOME: () => (0, operations_1.armHomeOperation)(this.alarmCode, this.forceArm),
102
+ DISARMED: () => (0, operations_1.disarmOperation)(this.alarmCode),
103
+ }[targetArmState]();
104
+ const { transactionId } = await this.installation.client(operation);
105
+ await this.resolveChangeResult((0, operations_1.pollArmStateOperation)(transactionId, targetArmState));
106
+ this.lastKnownState = value;
107
+ // A stale cached overview from just before this change must not answer
108
+ // the read HomeKit does right after to confirm it (see OverviewPoller#invalidate).
109
+ this.platform.poller(this.installation).invalidate();
110
+ setImmediate(() => {
111
+ this.service.updateCharacteristic(this.hap.Characteristic.SecuritySystemCurrentState, value);
112
+ });
113
+ }
114
+ onOverview(overview) {
115
+ const { armState } = overview;
116
+ if (!armState) {
117
+ return;
118
+ }
119
+ try {
120
+ const value = this.resolveCurrentState(armState.statusType);
121
+ this.service.updateCharacteristic(this.hap.Characteristic.SecuritySystemCurrentState, value);
122
+ }
123
+ catch (error) {
124
+ this.logPrefixed(error.message, 'debug');
125
+ }
126
+ }
127
+ }
128
+ exports.Alarm = Alarm;
129
+ //# sourceMappingURL=alarm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alarm.js","sourceRoot":"","sources":["../../src/accessories/alarm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { Logger, PlatformAccessory, Service } from 'homebridge';
2
+ import type { GraphqlOperation, VerisureInstallation } from 'verisure';
3
+ import type { AccessoryType, Overview, VerisurePlatformConfig } from '../types';
4
+ import type { VerisurePlatform } from '../platform';
5
+ export interface AccessoryContext {
6
+ deviceType: AccessoryType;
7
+ deviceLabel?: string;
8
+ installationGiid: string;
9
+ }
10
+ /**
11
+ * Common plumbing shared by every accessory type: logging, the
12
+ * AccessoryInformation service, and the arm/lock change-result poller.
13
+ */
14
+ export declare abstract class VerisureAccessoryHandler {
15
+ protected readonly platform: VerisurePlatform;
16
+ protected readonly accessory: PlatformAccessory<AccessoryContext>;
17
+ protected readonly installation: VerisureInstallation;
18
+ protected readonly log: Logger;
19
+ protected readonly hap: VerisurePlatform['api']['hap'];
20
+ protected readonly platformConfig: VerisurePlatformConfig;
21
+ protected readonly serialNumber?: string;
22
+ protected readonly accessoryInformation: Service;
23
+ constructor(platform: VerisurePlatform, accessory: PlatformAccessory<AccessoryContext>, installation: VerisureInstallation);
24
+ /** Paints the accessory's current characteristics from an overview that
25
+ * was already fetched for discovery, so it doesn't sit at HAP's default
26
+ * value until the next poll tick fires. */
27
+ paint(overview: Overview): void;
28
+ protected logPrefixed(message: string, level?: 'info' | 'debug' | 'warn' | 'error'): void;
29
+ /** Called on every poll tick with a fresh overview. Implementations should
30
+ * push any new values to their characteristics via `updateValue()` and
31
+ * must not throw - a missing/malformed slice for this one device shouldn't
32
+ * take down anything else. */
33
+ protected abstract onOverview(overview: Overview): void;
34
+ /** Polls a change-result query until Verisure resolves the transaction
35
+ * (non-null result) or `CHANGE_RESULT_TIMEOUT_MS` elapses. The original
36
+ * implementation had no timeout, so a transaction Verisure never resolved
37
+ * would retry forever every 200ms. */
38
+ protected resolveChangeResult(operation: GraphqlOperation, deadline?: number): Promise<string>;
39
+ }
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VerisureAccessoryHandler = void 0;
4
+ const settings_1 = require("../settings");
5
+ /**
6
+ * Common plumbing shared by every accessory type: logging, the
7
+ * AccessoryInformation service, and the arm/lock change-result poller.
8
+ */
9
+ class VerisureAccessoryHandler {
10
+ platform;
11
+ accessory;
12
+ installation;
13
+ log;
14
+ hap;
15
+ platformConfig;
16
+ serialNumber;
17
+ accessoryInformation;
18
+ constructor(platform, accessory, installation) {
19
+ this.platform = platform;
20
+ this.accessory = accessory;
21
+ this.installation = installation;
22
+ this.log = platform.log;
23
+ this.hap = platform.api.hap;
24
+ this.platformConfig = platform.config;
25
+ this.serialNumber = accessory.context.deviceLabel;
26
+ const { Characteristic, Service } = this.hap;
27
+ this.accessoryInformation = this.accessory.getService(Service.AccessoryInformation)
28
+ ?? this.accessory.addService(Service.AccessoryInformation);
29
+ this.accessoryInformation
30
+ .setCharacteristic(Characteristic.Manufacturer, 'Verisure')
31
+ .setCharacteristic(Characteristic.SerialNumber, this.serialNumber ?? this.installation.giid);
32
+ this.platform.poller(this.installation).subscribe((overview) => this.onOverview(overview));
33
+ }
34
+ /** Paints the accessory's current characteristics from an overview that
35
+ * was already fetched for discovery, so it doesn't sit at HAP's default
36
+ * value until the next poll tick fires. */
37
+ paint(overview) {
38
+ this.onOverview(overview);
39
+ }
40
+ logPrefixed(message, level = 'info') {
41
+ this.log[level](`${this.installation.config.alias} ${this.accessory.displayName}: ${message}`);
42
+ }
43
+ /** Polls a change-result query until Verisure resolves the transaction
44
+ * (non-null result) or `CHANGE_RESULT_TIMEOUT_MS` elapses. The original
45
+ * implementation had no timeout, so a transaction Verisure never resolved
46
+ * would retry forever every 200ms. */
47
+ async resolveChangeResult(operation, deadline = Date.now() + settings_1.CHANGE_RESULT_TIMEOUT_MS) {
48
+ this.logPrefixed(`Resolving: ${operation.operationName}`, 'debug');
49
+ const { installation: { pollResult: { result } } } = await this.installation
50
+ .client(operation);
51
+ this.logPrefixed(`Got "${result}" back from: ${operation.operationName}`, 'debug');
52
+ if (result !== null) {
53
+ return result;
54
+ }
55
+ if (Date.now() >= deadline) {
56
+ throw new Error(`Timed out waiting for ${operation.operationName} to resolve.`);
57
+ }
58
+ await new Promise((resolve) => { setTimeout(resolve, settings_1.CHANGE_RESULT_POLL_INTERVAL_MS); });
59
+ return this.resolveChangeResult(operation, deadline);
60
+ }
61
+ }
62
+ exports.VerisureAccessoryHandler = VerisureAccessoryHandler;
63
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/accessories/base.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { PlatformAccessory } from 'homebridge';
2
+ import type { VerisureInstallation } from 'verisure';
3
+ import type { VerisurePlatform } from '../platform';
4
+ import type { Climate, Overview } from '../types';
5
+ import { VerisureAccessoryHandler } from './base';
6
+ import type { AccessoryContext } from './base';
7
+ export declare class ClimateSensor extends VerisureAccessoryHandler {
8
+ private temperatureService?;
9
+ private humidityService?;
10
+ constructor(platform: VerisurePlatform, accessory: PlatformAccessory<AccessoryContext>, installation: VerisureInstallation, initial: Climate);
11
+ private findDevice;
12
+ private getCurrentValue;
13
+ protected onOverview(overview: Overview): void;
14
+ }
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClimateSensor = void 0;
4
+ const base_1 = require("./base");
5
+ const isNumber = (value) => typeof value === 'number';
6
+ class ClimateSensor extends base_1.VerisureAccessoryHandler {
7
+ temperatureService;
8
+ humidityService;
9
+ constructor(platform, accessory, installation, initial) {
10
+ super(platform, accessory, installation);
11
+ const label = initial.device.gui?.label ?? 'UNKNOWN';
12
+ const { Characteristic, Service } = this.hap;
13
+ this.accessoryInformation.setCharacteristic(Characteristic.Model, label);
14
+ // Verisure reports an unsupported reading as an explicit `null`, not a
15
+ // missing field - e.g. VoiceBox devices have a `humidityValue: null`.
16
+ // Checking only `!== undefined` let `null` through, which HomeKit
17
+ // rejects outright for these characteristics.
18
+ if (isNumber(initial.temperatureValue)) {
19
+ this.temperatureService = this.accessory.getService(Service.TemperatureSensor)
20
+ ?? this.accessory.addService(Service.TemperatureSensor, accessory.displayName);
21
+ this.temperatureService.getCharacteristic(Characteristic.CurrentTemperature)
22
+ .setProps({ minValue: -40.0, maxValue: 60.0 })
23
+ .onGet(() => this.getCurrentValue('temperatureValue'));
24
+ }
25
+ if (isNumber(initial.humidityValue)) {
26
+ this.humidityService = this.accessory.getService(Service.HumiditySensor)
27
+ ?? this.accessory.addService(Service.HumiditySensor, accessory.displayName);
28
+ this.humidityService.getCharacteristic(Characteristic.CurrentRelativeHumidity)
29
+ .onGet(() => this.getCurrentValue('humidityValue'));
30
+ }
31
+ }
32
+ findDevice(overview) {
33
+ return overview.climates?.find((climate) => climate.device.deviceLabel === this.serialNumber);
34
+ }
35
+ async getCurrentValue(property) {
36
+ this.logPrefixed(`Getting current ${property} value.`, 'debug');
37
+ const overview = await this.platform.poller(this.installation).getOverview();
38
+ const device = this.findDevice(overview);
39
+ const value = device?.[property];
40
+ if (!isNumber(value)) {
41
+ throw new this.hap.HapStatusError(-70402 /* this.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
42
+ }
43
+ return value;
44
+ }
45
+ onOverview(overview) {
46
+ const device = this.findDevice(overview);
47
+ if (!device) {
48
+ return;
49
+ }
50
+ const { Characteristic } = this.hap;
51
+ if (this.temperatureService && isNumber(device.temperatureValue)) {
52
+ this.temperatureService.updateCharacteristic(Characteristic.CurrentTemperature, device.temperatureValue);
53
+ }
54
+ if (this.humidityService && isNumber(device.humidityValue)) {
55
+ this.humidityService.updateCharacteristic(Characteristic.CurrentRelativeHumidity, device.humidityValue);
56
+ }
57
+ }
58
+ }
59
+ exports.ClimateSensor = ClimateSensor;
60
+ //# sourceMappingURL=climateSensor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"climateSensor.js","sourceRoot":"","sources":["../../src/accessories/climateSensor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { PlatformAccessory } from 'homebridge';
2
+ import type { VerisureInstallation } from 'verisure';
3
+ import type { VerisurePlatform } from '../platform';
4
+ import type { Overview } from '../types';
5
+ import { VerisureAccessoryHandler } from './base';
6
+ import type { AccessoryContext } from './base';
7
+ export declare class ContactSensor extends VerisureAccessoryHandler {
8
+ private readonly service;
9
+ constructor(platform: VerisurePlatform, accessory: PlatformAccessory<AccessoryContext>, installation: VerisureInstallation);
10
+ private static resolveSensorState;
11
+ private findDevice;
12
+ private getCurrentSensorState;
13
+ protected onOverview(overview: Overview): void;
14
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContactSensor = void 0;
4
+ const base_1 = require("./base");
5
+ class ContactSensor extends base_1.VerisureAccessoryHandler {
6
+ service;
7
+ constructor(platform, accessory, installation) {
8
+ super(platform, accessory, installation);
9
+ const { Service, Characteristic } = this.hap;
10
+ this.service = this.accessory.getService(Service.ContactSensor)
11
+ ?? this.accessory.addService(Service.ContactSensor, accessory.displayName);
12
+ this.service.getCharacteristic(Characteristic.ContactSensorState)
13
+ .onGet(() => this.getCurrentSensorState());
14
+ }
15
+ static resolveSensorState(input) {
16
+ return input !== 'CLOSE';
17
+ }
18
+ findDevice(overview) {
19
+ return overview.doorWindows?.find((doorWindow) => doorWindow.device.deviceLabel === this.serialNumber);
20
+ }
21
+ async getCurrentSensorState() {
22
+ this.logPrefixed('Getting current sensor state.', 'debug');
23
+ const overview = await this.platform.poller(this.installation).getOverview();
24
+ const doorWindow = this.findDevice(overview);
25
+ if (!doorWindow) {
26
+ throw new this.hap.HapStatusError(-70402 /* this.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
27
+ }
28
+ return ContactSensor.resolveSensorState(doorWindow.state);
29
+ }
30
+ onOverview(overview) {
31
+ const doorWindow = this.findDevice(overview);
32
+ if (!doorWindow) {
33
+ return;
34
+ }
35
+ this.service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, ContactSensor.resolveSensorState(doorWindow.state));
36
+ }
37
+ }
38
+ exports.ContactSensor = ContactSensor;
39
+ //# sourceMappingURL=contactSensor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contactSensor.js","sourceRoot":"","sources":["../../src/accessories/contactSensor.ts"],"names":[],"mappings":";;;AAKA,iCAAkD;AAGlD,MAAa,aAAc,SAAQ,+BAAwB;IACxC,OAAO,CAAU;IAElC,YACE,QAA0B,EAC1B,SAA8C,EAC9C,YAAkC;QAElC,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;QAEzC,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC;QAC7C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,CAAC;eAC1D,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;QAE7E,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,cAAc,CAAC,kBAAkB,CAAC;aAC9D,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,qBAAqB,EAAE,CAAC,CAAC;IAC/C,CAAC;IAEO,MAAM,CAAC,kBAAkB,CAAC,KAAa;QAC7C,OAAO,KAAK,KAAK,OAAO,CAAC;IAC3B,CAAC;IAEO,UAAU,CAAC,QAAkB;QACnC,OAAO,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,WAAW,KAAK,IAAI,CAAC,YAAY,CAAC,CAAC;IACzG,CAAC;IAEO,KAAK,CAAC,qBAAqB;QACjC,IAAI,CAAC,WAAW,CAAC,+BAA+B,EAAE,OAAO,CAAC,CAAC;QAC3D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7E,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,cAAc,+DAAkD,CAAC;QACtF,CAAC;QACD,OAAO,aAAa,CAAC,kBAAkB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAC5D,CAAC;IAES,UAAU,CAAC,QAAkB;QACrC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC7C,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO;QACT,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAC/B,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,kBAAkB,EAC1C,aAAa,CAAC,kBAAkB,CAAC,UAAU,CAAC,KAAK,CAAC,CACnD,CAAC;IACJ,CAAC;CACF;AA9CD,sCA8CC"}
@@ -0,0 +1,32 @@
1
+ import type { PlatformAccessory } from 'homebridge';
2
+ import type { VerisureInstallation } from 'verisure';
3
+ import type { VerisurePlatform } from '../platform';
4
+ import type { Overview } from '../types';
5
+ import { VerisureAccessoryHandler } from './base';
6
+ import type { AccessoryContext } from './base';
7
+ export declare class DoorLock extends VerisureAccessoryHandler {
8
+ private readonly lockService;
9
+ private readonly autoLockService?;
10
+ private readonly audioService?;
11
+ private readonly doorCode;
12
+ /** The lock state we're actively trying to reach, if any. Cleared as soon
13
+ * as the change settles (success or failure) so getTargetLockState() falls
14
+ * straight back to the device's actual state - keeping it "stuck" here
15
+ * indefinitely was the cause of the Yale-lock report/reconcile loop that
16
+ * burned through the daily API quota in issues #159, #173, #181, #168. */
17
+ private pendingTargetState?;
18
+ private readonly getLockConfig;
19
+ constructor(platform: VerisurePlatform, accessory: PlatformAccessory<AccessoryContext>, installation: VerisureInstallation);
20
+ private resolveCurrentLockState;
21
+ private findDevice;
22
+ private getDoorLock;
23
+ private getCurrentLockState;
24
+ private getTargetLockState;
25
+ private setTargetLockState;
26
+ private resolveAutoLockState;
27
+ private getAutoLockState;
28
+ private setAutoLockState;
29
+ private getAudioState;
30
+ private setAudioState;
31
+ protected onOverview(overview: Overview): void;
32
+ }