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.
- package/README.md +33 -2
- package/config.schema.json +30 -0
- package/dist/accessories/alarm.d.ts +28 -0
- package/dist/accessories/alarm.js +129 -0
- package/dist/accessories/alarm.js.map +1 -0
- package/dist/accessories/base.d.ts +39 -0
- package/dist/accessories/base.js +63 -0
- package/dist/accessories/base.js.map +1 -0
- package/dist/accessories/climateSensor.d.ts +14 -0
- package/dist/accessories/climateSensor.js +60 -0
- package/dist/accessories/climateSensor.js.map +1 -0
- package/dist/accessories/contactSensor.d.ts +14 -0
- package/dist/accessories/contactSensor.js +39 -0
- package/dist/accessories/contactSensor.js.map +1 -0
- package/dist/accessories/doorLock.d.ts +32 -0
- package/dist/accessories/doorLock.js +162 -0
- package/dist/accessories/doorLock.js.map +1 -0
- package/dist/accessories/smartPlug.d.ts +15 -0
- package/dist/accessories/smartPlug.js +54 -0
- package/dist/accessories/smartPlug.js.map +1 -0
- package/dist/bin/verisure.d.ts +2 -0
- package/dist/bin/verisure.js +56 -0
- package/dist/bin/verisure.js.map +1 -0
- package/dist/cache.d.ts +14 -0
- package/dist/cache.js +35 -0
- package/dist/cache.js.map +1 -0
- package/dist/deviceNames.d.ts +3 -0
- package/dist/deviceNames.js +16 -0
- package/dist/deviceNames.js.map +1 -0
- package/dist/i18n/index.d.ts +1 -0
- package/dist/i18n/index.js +18 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/nb_NO.json +22 -0
- package/dist/i18n/sv_SE.json +22 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/operations.d.ts +27 -0
- package/dist/operations.js +322 -0
- package/dist/operations.js.map +1 -0
- package/dist/overviewPoller.d.ts +53 -0
- package/dist/overviewPoller.js +169 -0
- package/dist/overviewPoller.js.map +1 -0
- package/dist/platform.d.ts +27 -0
- package/dist/platform.js +272 -0
- package/dist/platform.js.map +1 -0
- package/dist/settings.d.ts +21 -0
- package/dist/settings.js +25 -0
- package/dist/settings.js.map +1 -0
- package/dist/types.d.ts +117 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/package.json +39 -16
- package/CHANGELOG.md +0 -174
- package/bin/verisure +0 -55
- package/index.js +0 -6
- package/lib/accessories/alarm.js +0 -111
- package/lib/accessories/climatesensor.js +0 -68
- package/lib/accessories/contactsensor.js +0 -45
- package/lib/accessories/doorlock.js +0 -216
- package/lib/accessories/index.js +0 -13
- package/lib/accessories/smartplug.js +0 -60
- package/lib/accessories/verisure.js +0 -62
- package/lib/i18n/index.js +0 -11
- package/lib/i18n/nb_NO.json +0 -22
- package/lib/i18n/sv_SE.json +0 -22
- package/lib/operations.js +0 -225
- package/lib/platform.js +0 -156
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
[](https://github.com/ptz0n/homebridge-verisure/actions?query=workflow%3ATest)
|
|
8
8
|
|
|
9
|
-
This is a plugin for [Homebridge](https://
|
|
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
|
+
```
|
package/config.schema.json
CHANGED
|
@@ -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
|
+
}
|