hoffmation-base 3.6.1 → 3.7.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/lib/action/airQualitySensorChangeAction.d.ts +16 -0
- package/lib/action/airQualitySensorChangeAction.js +15 -0
- package/lib/action/index.d.ts +1 -0
- package/lib/action/index.js +3 -1
- package/lib/devices/sharedFunctions/airQualitySensor.d.ts +27 -0
- package/lib/devices/sharedFunctions/airQualitySensor.js +87 -0
- package/lib/devices/sharedFunctions/index.d.ts +1 -0
- package/lib/devices/sharedFunctions/index.js +1 -0
- package/lib/devices/unifi/index.d.ts +1 -0
- package/lib/devices/unifi/index.js +1 -0
- package/lib/devices/unifi/own-unifi-air-quality-sensor.d.ts +81 -0
- package/lib/devices/unifi/own-unifi-air-quality-sensor.js +164 -0
- package/lib/devices/unifi/own-unifi-camera.d.ts +6 -5
- package/lib/devices/unifi/own-unifi-camera.js +14 -24
- package/lib/devices/unifi/protect-module.d.ts +11 -0
- package/lib/devices/unifi/protect-module.js +16 -0
- package/lib/devices/unifi/unifi-logger.d.ts +5 -5
- package/lib/devices/unifi/unifi-logger.js +9 -8
- package/lib/devices/unifi/unifi-protect.d.ts +38 -7
- package/lib/devices/unifi/unifi-protect.js +199 -66
- package/lib/enums/DeviceCapability.d.ts +1 -0
- package/lib/enums/DeviceCapability.js +1 -0
- package/lib/enums/commandType.d.ts +1 -0
- package/lib/enums/commandType.js +1 -0
- package/lib/enums/deviceType.d.ts +1 -0
- package/lib/enums/deviceType.js +1 -0
- package/lib/interfaces/baseDevices/iAirQualityCollector.d.ts +23 -0
- package/lib/interfaces/baseDevices/iAirQualityCollector.js +2 -0
- package/lib/interfaces/baseDevices/iAirQualitySensor.d.ts +97 -0
- package/lib/interfaces/baseDevices/iAirQualitySensor.js +7 -0
- package/lib/interfaces/baseDevices/index.d.ts +2 -0
- package/lib/interfaces/baseDevices/index.js +2 -0
- package/lib/interfaces/iPersist.d.ts +6 -1
- package/lib/services/dbo/postgreSqlPersist.d.ts +3 -1
- package/lib/services/dbo/postgreSqlPersist.js +34 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -3
|
@@ -1,22 +1,53 @@
|
|
|
1
|
+
import { OwnUnifiAirQualitySensor } from './own-unifi-air-quality-sensor';
|
|
1
2
|
import { OwnUnifiCamera } from './own-unifi-camera';
|
|
2
3
|
import { iDisposable, iUnifiProtectOptions } from '../../interfaces';
|
|
3
4
|
export declare class UnifiProtect implements iDisposable {
|
|
4
5
|
private readonly unifiLogger;
|
|
5
|
-
private readonly _api;
|
|
6
6
|
/**
|
|
7
7
|
* Mapping for own devices
|
|
8
8
|
*/
|
|
9
9
|
static readonly ownCameras: Map<string, OwnUnifiCamera>;
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Mapping for own air quality sensors
|
|
12
|
+
*/
|
|
13
|
+
static readonly ownAirQualitySensors: Map<string, OwnUnifiAirQualitySensor>;
|
|
14
|
+
private _client;
|
|
15
|
+
private _connecting;
|
|
16
|
+
private _disposed;
|
|
17
|
+
private _eventAbort;
|
|
11
18
|
private _idMap;
|
|
12
|
-
private
|
|
13
|
-
private
|
|
19
|
+
private _ignoredIds;
|
|
20
|
+
private _sensorIdMap;
|
|
21
|
+
private _ignoredSensorIds;
|
|
14
22
|
constructor(settings: iUnifiProtectOptions);
|
|
15
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Connects to the NVR. A single atomic operation which logs in, bootstraps and opens the realtime channel.
|
|
25
|
+
* @param settings - The address and credentials of the NVR
|
|
26
|
+
*/
|
|
27
|
+
private connect;
|
|
28
|
+
/**
|
|
29
|
+
* Renders a typed `ProtectError` including its errno code and the whole cause chain,
|
|
30
|
+
* as the message alone rarely names the actual failure.
|
|
31
|
+
* @param error - The rejected value
|
|
32
|
+
* @returns A single line description
|
|
33
|
+
*/
|
|
34
|
+
private static describeError;
|
|
16
35
|
dispose(): void;
|
|
17
36
|
static addDevice(camera: OwnUnifiCamera): void;
|
|
37
|
+
/**
|
|
38
|
+
* Registers an air quality sensor to be fed with the readings of its Unifi counterpart.
|
|
39
|
+
* @param sensor - The device to register
|
|
40
|
+
*/
|
|
41
|
+
static addAirQualitySensor(sensor: OwnUnifiAirQualitySensor): void;
|
|
18
42
|
private initialize;
|
|
19
|
-
|
|
20
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Consumes the realtime firehose until the client is disposed or the stream dies.
|
|
45
|
+
* @param client - The connected client whose event stream is consumed
|
|
46
|
+
*/
|
|
47
|
+
private consumeEvents;
|
|
48
|
+
private onEvent;
|
|
49
|
+
private forwardToCamera;
|
|
50
|
+
private ownSensorFor;
|
|
51
|
+
private initializeSensor;
|
|
21
52
|
private initializeCamera;
|
|
22
53
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.UnifiProtect = void 0;
|
|
4
|
-
const
|
|
4
|
+
const protect_module_1 = require("./protect-module");
|
|
5
5
|
const enums_1 = require("../../enums");
|
|
6
6
|
const logging_1 = require("../../logging");
|
|
7
7
|
const utils_1 = require("../../utils");
|
|
@@ -9,105 +9,234 @@ const unifi_logger_1 = require("./unifi-logger");
|
|
|
9
9
|
class UnifiProtect {
|
|
10
10
|
constructor(settings) {
|
|
11
11
|
this.unifiLogger = new unifi_logger_1.UnifiLogger(enums_1.LogSource.UnifiProtect);
|
|
12
|
-
this.
|
|
12
|
+
this._client = null;
|
|
13
|
+
this._connecting = false;
|
|
14
|
+
this._disposed = false;
|
|
15
|
+
this._eventAbort = null;
|
|
13
16
|
this._idMap = new Map();
|
|
14
|
-
this.
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
17
|
+
this._ignoredIds = new Set();
|
|
18
|
+
this._sensorIdMap = new Map();
|
|
19
|
+
this._ignoredSensorIds = new Set();
|
|
20
|
+
this.connect(settings);
|
|
18
21
|
utils_1.Utils.guardedInterval(() => {
|
|
19
|
-
if (
|
|
20
|
-
//
|
|
22
|
+
if (this._disposed || this._connecting || this._client !== null) {
|
|
23
|
+
// Either shut down, already connecting or the client is up and recovers outages on its own.
|
|
21
24
|
return;
|
|
22
25
|
}
|
|
23
|
-
this.
|
|
26
|
+
this.connect(settings);
|
|
24
27
|
}, 5 * 60 * 1000);
|
|
25
28
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Connects to the NVR. A single atomic operation which logs in, bootstraps and opens the realtime channel.
|
|
31
|
+
* @param settings - The address and credentials of the NVR
|
|
32
|
+
*/
|
|
33
|
+
connect(settings) {
|
|
34
|
+
this._connecting = true;
|
|
35
|
+
this._idMap = new Map();
|
|
36
|
+
this._ignoredIds = new Set();
|
|
37
|
+
this._sensorIdMap = new Map();
|
|
38
|
+
this._ignoredSensorIds = new Set();
|
|
39
|
+
(0, protect_module_1.loadProtectModule)()
|
|
40
|
+
.then((protect) => protect.ProtectClient.connect({
|
|
41
|
+
host: settings.nvrAddress,
|
|
42
|
+
username: settings.username,
|
|
43
|
+
password: settings.password,
|
|
44
|
+
log: this.unifiLogger,
|
|
45
|
+
}))
|
|
46
|
+
.then((client) => {
|
|
47
|
+
this._connecting = false;
|
|
48
|
+
if (this._disposed) {
|
|
49
|
+
void client[Symbol.asyncDispose]();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this._client = client;
|
|
53
|
+
this.initialize(client);
|
|
32
54
|
})
|
|
33
55
|
.catch((error) => {
|
|
34
|
-
|
|
56
|
+
this._connecting = false;
|
|
57
|
+
logging_1.ServerLogService.writeLog(enums_1.LogLevel.Error, `Unifi-Protect: Login failed: ${UnifiProtect.describeError(error)}`);
|
|
35
58
|
});
|
|
36
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Renders a typed `ProtectError` including its errno code and the whole cause chain,
|
|
62
|
+
* as the message alone rarely names the actual failure.
|
|
63
|
+
* @param error - The rejected value
|
|
64
|
+
* @returns A single line description
|
|
65
|
+
*/
|
|
66
|
+
static describeError(error) {
|
|
67
|
+
if (!(error instanceof Error)) {
|
|
68
|
+
return `${error}`;
|
|
69
|
+
}
|
|
70
|
+
const parts = [`${error.name}: ${error.message}`];
|
|
71
|
+
const code = Reflect.get(error, 'code');
|
|
72
|
+
if (typeof code === 'string') {
|
|
73
|
+
parts.push(`code: ${code}`);
|
|
74
|
+
}
|
|
75
|
+
let cause = error.cause;
|
|
76
|
+
while (cause instanceof Error) {
|
|
77
|
+
parts.push(`caused by ${cause.name}: ${cause.message}`);
|
|
78
|
+
cause = cause.cause;
|
|
79
|
+
}
|
|
80
|
+
return parts.join(' | ');
|
|
81
|
+
}
|
|
37
82
|
dispose() {
|
|
38
|
-
|
|
83
|
+
var _a;
|
|
84
|
+
this._disposed = true;
|
|
85
|
+
(_a = this._eventAbort) === null || _a === void 0 ? void 0 : _a.abort();
|
|
86
|
+
this._eventAbort = null;
|
|
87
|
+
const client = this._client;
|
|
88
|
+
this._client = null;
|
|
89
|
+
client === null || client === void 0 ? void 0 : client[Symbol.asyncDispose]().catch((error) => {
|
|
90
|
+
logging_1.ServerLogService.writeLog(enums_1.LogLevel.Error, `Unifi-Protect: Disposal failed: ${error}`);
|
|
91
|
+
});
|
|
39
92
|
}
|
|
40
93
|
static addDevice(camera) {
|
|
41
94
|
this.ownCameras.set(camera.unifiCameraName, camera);
|
|
42
95
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Registers an air quality sensor to be fed with the readings of its Unifi counterpart.
|
|
98
|
+
* @param sensor - The device to register
|
|
99
|
+
*/
|
|
100
|
+
static addAirQualitySensor(sensor) {
|
|
101
|
+
this.ownAirQualitySensors.set(sensor.unifiSensorName, sensor);
|
|
102
|
+
}
|
|
103
|
+
initialize(client) {
|
|
104
|
+
this.unifiLogger.info(`Unifi-Protect: Connected to "${client.controllerName}"`);
|
|
105
|
+
for (const camera of client.cameras) {
|
|
52
106
|
this.initializeCamera(camera);
|
|
53
107
|
}
|
|
54
|
-
|
|
108
|
+
for (const sensor of client.sensors) {
|
|
109
|
+
this.initializeSensor(sensor);
|
|
110
|
+
}
|
|
111
|
+
void this.consumeEvents(client);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Consumes the realtime firehose until the client is disposed or the stream dies.
|
|
115
|
+
* @param client - The connected client whose event stream is consumed
|
|
116
|
+
*/
|
|
117
|
+
async consumeEvents(client) {
|
|
118
|
+
const abort = new AbortController();
|
|
119
|
+
this._eventAbort = abort;
|
|
120
|
+
try {
|
|
121
|
+
for await (const event of client.events({ signal: abort.signal })) {
|
|
122
|
+
this.onEvent(client, event);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
if (!abort.signal.aborted) {
|
|
127
|
+
logging_1.ServerLogService.writeLog(enums_1.LogLevel.Error, `Unifi-Protect: Event stream failed: ${error}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (this._client === client) {
|
|
131
|
+
// The stream is the client's lifeline --> drop it, so the interval reconnects.
|
|
132
|
+
this._client = null;
|
|
133
|
+
void client[Symbol.asyncDispose]();
|
|
134
|
+
}
|
|
55
135
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
136
|
+
onEvent(client, event) {
|
|
137
|
+
switch (event.kind) {
|
|
138
|
+
case 'bootstrapLoaded':
|
|
139
|
+
for (const camera of client.cameras) {
|
|
140
|
+
this.initializeCamera(camera);
|
|
141
|
+
}
|
|
142
|
+
for (const sensor of client.sensors) {
|
|
143
|
+
this.initializeSensor(sensor);
|
|
144
|
+
}
|
|
62
145
|
break;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
id = (_a = addEvent.camera) !== null && _a !== void 0 ? _a : addEvent.cameraId;
|
|
70
|
-
this.rememberAddEvent(packet, addEvent);
|
|
146
|
+
case 'deviceAdded': {
|
|
147
|
+
if (event.modelKey === 'camera') {
|
|
148
|
+
const camera = client.camera(event.id);
|
|
149
|
+
if (camera !== undefined) {
|
|
150
|
+
this.initializeCamera(camera);
|
|
151
|
+
}
|
|
71
152
|
}
|
|
72
|
-
else if (
|
|
73
|
-
|
|
74
|
-
|
|
153
|
+
else if (event.modelKey === 'sensor') {
|
|
154
|
+
const sensor = client.sensor(event.id);
|
|
155
|
+
if (sensor !== undefined) {
|
|
156
|
+
this.initializeSensor(sensor);
|
|
157
|
+
}
|
|
75
158
|
}
|
|
76
|
-
|
|
77
|
-
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
case 'devicePatched': {
|
|
162
|
+
if (event.modelKey !== 'sensor') {
|
|
78
163
|
break;
|
|
79
164
|
}
|
|
80
|
-
|
|
81
|
-
|
|
165
|
+
// The patch carries the full airQuality block, but the projection is authoritative and always complete.
|
|
166
|
+
const ownSensor = this.ownSensorFor(event.id);
|
|
167
|
+
const sensor = client.sensor(event.id);
|
|
168
|
+
if (ownSensor !== undefined && sensor !== undefined) {
|
|
82
169
|
utils_1.Utils.guardedFunction(() => {
|
|
83
|
-
|
|
170
|
+
ownSensor.update(sensor.config);
|
|
84
171
|
}, this);
|
|
85
|
-
break;
|
|
86
172
|
}
|
|
87
173
|
break;
|
|
174
|
+
}
|
|
175
|
+
case 'deviceRemoved':
|
|
176
|
+
this._idMap.delete(event.id);
|
|
177
|
+
this._ignoredIds.delete(event.id);
|
|
178
|
+
this._sensorIdMap.delete(event.id);
|
|
179
|
+
this._ignoredSensorIds.delete(event.id);
|
|
180
|
+
break;
|
|
181
|
+
case 'motionDetected':
|
|
182
|
+
case 'smartDetect':
|
|
183
|
+
this.forwardToCamera(event.cameraId, event);
|
|
184
|
+
break;
|
|
88
185
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
186
|
+
}
|
|
187
|
+
forwardToCamera(cameraId, event) {
|
|
188
|
+
const ownName = this._idMap.get(cameraId);
|
|
189
|
+
if (!ownName) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const ownCamera = UnifiProtect.ownCameras.get(ownName);
|
|
193
|
+
if (ownCamera === undefined) {
|
|
194
|
+
return;
|
|
92
195
|
}
|
|
196
|
+
utils_1.Utils.guardedFunction(() => {
|
|
197
|
+
ownCamera.update(event);
|
|
198
|
+
}, this);
|
|
93
199
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
this._eventMap.delete(addEvent.id);
|
|
99
|
-
}, 5 * 60 * 1000, this);
|
|
200
|
+
ownSensorFor(sensorId) {
|
|
201
|
+
const ownName = this._sensorIdMap.get(sensorId);
|
|
202
|
+
if (!ownName) {
|
|
203
|
+
return undefined;
|
|
100
204
|
}
|
|
205
|
+
return UnifiProtect.ownAirQualitySensors.get(ownName);
|
|
101
206
|
}
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
207
|
+
initializeSensor(sensor) {
|
|
208
|
+
if (this._sensorIdMap.has(sensor.id) || this._ignoredSensorIds.has(sensor.id)) {
|
|
209
|
+
// Already known --> the projection is live, so there is nothing to refresh.
|
|
105
210
|
return;
|
|
106
211
|
}
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
212
|
+
const name = sensor.name;
|
|
213
|
+
if (!name || !UnifiProtect.ownAirQualitySensors.has(name)) {
|
|
214
|
+
this._ignoredSensorIds.add(sensor.id);
|
|
215
|
+
logging_1.ServerLogService.writeLog(enums_1.LogLevel.Info, `Unifi-Protect: Ignoring sensor ${name}`);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const ownSensor = UnifiProtect.ownAirQualitySensors.get(name);
|
|
219
|
+
this._sensorIdMap.set(sensor.id, name);
|
|
220
|
+
utils_1.Utils.guardedFunction(() => {
|
|
221
|
+
ownSensor.initialize(sensor);
|
|
222
|
+
}, this);
|
|
223
|
+
logging_1.ServerLogService.writeLog(enums_1.LogLevel.Info, `Unifi-Protect: Sensor ${name} (re)initialized`);
|
|
224
|
+
}
|
|
225
|
+
initializeCamera(camera) {
|
|
226
|
+
if (this._idMap.has(camera.id) || this._ignoredIds.has(camera.id)) {
|
|
227
|
+
// Already known --> the projection is live, so there is nothing to refresh.
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const name = camera.name;
|
|
231
|
+
if (!name || !UnifiProtect.ownCameras.has(name)) {
|
|
232
|
+
this._ignoredIds.add(camera.id);
|
|
233
|
+
logging_1.ServerLogService.writeLog(enums_1.LogLevel.Info, `Unifi-Protect: Ignoring camera ${name}`);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const ownCamera = UnifiProtect.ownCameras.get(name);
|
|
237
|
+
ownCamera.initialize(camera);
|
|
238
|
+
logging_1.ServerLogService.writeLog(enums_1.LogLevel.Info, `Unifi-Protect: Camera ${name} (re)initialized`);
|
|
239
|
+
this._idMap.set(camera.id, name);
|
|
111
240
|
}
|
|
112
241
|
}
|
|
113
242
|
exports.UnifiProtect = UnifiProtect;
|
|
@@ -115,3 +244,7 @@ exports.UnifiProtect = UnifiProtect;
|
|
|
115
244
|
* Mapping for own devices
|
|
116
245
|
*/
|
|
117
246
|
UnifiProtect.ownCameras = new Map();
|
|
247
|
+
/**
|
|
248
|
+
* Mapping for own air quality sensors
|
|
249
|
+
*/
|
|
250
|
+
UnifiProtect.ownAirQualitySensors = new Map();
|
|
@@ -26,6 +26,7 @@ var DeviceCapability;
|
|
|
26
26
|
DeviceCapability[DeviceCapability["loadMetering"] = 20] = "loadMetering";
|
|
27
27
|
DeviceCapability[DeviceCapability["garageDoorOpener"] = 21] = "garageDoorOpener";
|
|
28
28
|
DeviceCapability[DeviceCapability["magnetSensor"] = 22] = "magnetSensor";
|
|
29
|
+
DeviceCapability[DeviceCapability["airQualitySensor"] = 23] = "airQualitySensor";
|
|
29
30
|
DeviceCapability[DeviceCapability["bluetoothDetector"] = 101] = "bluetoothDetector";
|
|
30
31
|
DeviceCapability[DeviceCapability["trackableDevice"] = 102] = "trackableDevice";
|
|
31
32
|
DeviceCapability[DeviceCapability["scene"] = 103] = "scene";
|
|
@@ -14,6 +14,7 @@ export declare enum CommandType {
|
|
|
14
14
|
ActuatorWriteStateToDeviceCommand = "ActuatorWriteStateToDeviceCommand",
|
|
15
15
|
DimmerSetLightCommand = "DimmerSetLightCommand",
|
|
16
16
|
FloorSetAllShuttersCommand = "FloorSetAllShuttersCommand",
|
|
17
|
+
AirQualitySensorChangeAction = "AirQualitySensorChangeAction",
|
|
17
18
|
HumiditySensorChangeAction = "HumiditySensorChangeAction",
|
|
18
19
|
LampSetLightCommand = "LampSetLightCommand",
|
|
19
20
|
LampSetTimeBasedCommand = "LampSetTimeBasedCommand",
|
package/lib/enums/commandType.js
CHANGED
|
@@ -18,6 +18,7 @@ var CommandType;
|
|
|
18
18
|
CommandType["ActuatorWriteStateToDeviceCommand"] = "ActuatorWriteStateToDeviceCommand";
|
|
19
19
|
CommandType["DimmerSetLightCommand"] = "DimmerSetLightCommand";
|
|
20
20
|
CommandType["FloorSetAllShuttersCommand"] = "FloorSetAllShuttersCommand";
|
|
21
|
+
CommandType["AirQualitySensorChangeAction"] = "AirQualitySensorChangeAction";
|
|
21
22
|
CommandType["HumiditySensorChangeAction"] = "HumiditySensorChangeAction";
|
|
22
23
|
CommandType["LampSetLightCommand"] = "LampSetLightCommand";
|
|
23
24
|
CommandType["LampSetTimeBasedCommand"] = "LampSetTimeBasedCommand";
|
package/lib/enums/deviceType.js
CHANGED
|
@@ -62,6 +62,7 @@ var DeviceType;
|
|
|
62
62
|
DeviceType[DeviceType["TrackableDevice"] = 4002] = "TrackableDevice";
|
|
63
63
|
DeviceType[DeviceType["Camera"] = 6001] = "Camera";
|
|
64
64
|
DeviceType[DeviceType["Door"] = 6002] = "Door";
|
|
65
|
+
DeviceType[DeviceType["UnifiAirQualitySensor"] = 6003] = "UnifiAirQualitySensor";
|
|
65
66
|
DeviceType[DeviceType["Victron"] = 7001] = "Victron";
|
|
66
67
|
DeviceType[DeviceType["Dachs"] = 7002] = "Dachs";
|
|
67
68
|
DeviceType[DeviceType["DachsWarmWaterTemperature"] = 7003] = "DachsWarmWaterTemperature";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { iRoomDevice } from './iRoomDevice';
|
|
2
|
+
import { AirQualitySensorChangeAction } from '../../action';
|
|
3
|
+
import { iAirQualityReadings, iAirQualitySensor } from './iAirQualitySensor';
|
|
4
|
+
/**
|
|
5
|
+
* This interface represents a device measuring air quality.
|
|
6
|
+
*
|
|
7
|
+
* For devices with {@link DeviceCapability.airQualitySensor} capability.
|
|
8
|
+
*/
|
|
9
|
+
export interface iAirQualityCollector extends iRoomDevice {
|
|
10
|
+
/**
|
|
11
|
+
* Service which handles common aspects of the air quality sensor like persisting
|
|
12
|
+
*/
|
|
13
|
+
readonly airQualitySensor: iAirQualitySensor;
|
|
14
|
+
/**
|
|
15
|
+
* The most recent air quality readings of this device
|
|
16
|
+
*/
|
|
17
|
+
readonly airQuality: iAirQualityReadings;
|
|
18
|
+
/**
|
|
19
|
+
* Add a callback that is called when any air quality reading changes
|
|
20
|
+
* @param pCallback - The callback to fire
|
|
21
|
+
*/
|
|
22
|
+
addAirQualityCallback(pCallback: (action: AirQualitySensorChangeAction) => void): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { iJsonOmitKeys } from '../iJsonOmitKeys';
|
|
2
|
+
import { AirQualitySensorChangeAction } from '../../action';
|
|
3
|
+
/**
|
|
4
|
+
* The value reported for a metric the sensor does not measure or has not reported yet.
|
|
5
|
+
*/
|
|
6
|
+
export declare const UNDEFINED_AIR_QUALITY_VALUE = -1;
|
|
7
|
+
/**
|
|
8
|
+
* The set of air quality metrics a sensor can report.
|
|
9
|
+
*
|
|
10
|
+
* Devices differ widely in what they measure, so every member is always present but defaults to
|
|
11
|
+
* {@link UNDEFINED_AIR_QUALITY_VALUE} until the device reports it. This mirrors the sentinel approach of
|
|
12
|
+
* {@link UNDEFINED_TEMP_VALUE} and spares consumers a null check on every metric; use
|
|
13
|
+
* {@link iAirQualitySensor.reports} to tell "not measured" apart from a measured zero.
|
|
14
|
+
*/
|
|
15
|
+
export interface iAirQualityReadings {
|
|
16
|
+
/**
|
|
17
|
+
* The air quality index as calculated by the device (lower is better)
|
|
18
|
+
*/
|
|
19
|
+
aqi: number;
|
|
20
|
+
/**
|
|
21
|
+
* The carbon dioxide concentration in ppm
|
|
22
|
+
*/
|
|
23
|
+
co2: number;
|
|
24
|
+
/**
|
|
25
|
+
* The nitrogen oxide index (1 equals background level)
|
|
26
|
+
*/
|
|
27
|
+
nox: number;
|
|
28
|
+
/**
|
|
29
|
+
* Particulate matter up to 1.0 µm in µg/m³
|
|
30
|
+
*/
|
|
31
|
+
pm1p0: number;
|
|
32
|
+
/**
|
|
33
|
+
* Particulate matter up to 2.5 µm in µg/m³
|
|
34
|
+
*/
|
|
35
|
+
pm2p5: number;
|
|
36
|
+
/**
|
|
37
|
+
* Particulate matter up to 4.0 µm in µg/m³
|
|
38
|
+
*/
|
|
39
|
+
pm4p0: number;
|
|
40
|
+
/**
|
|
41
|
+
* Particulate matter up to 10.0 µm in µg/m³
|
|
42
|
+
*/
|
|
43
|
+
pm10p0: number;
|
|
44
|
+
/**
|
|
45
|
+
* The total volatile organic compounds in ppb
|
|
46
|
+
*/
|
|
47
|
+
tvoc: number;
|
|
48
|
+
/**
|
|
49
|
+
* The vape detection reading of the device
|
|
50
|
+
*/
|
|
51
|
+
vape: number;
|
|
52
|
+
/**
|
|
53
|
+
* The volatile organic compounds index (100 equals background level)
|
|
54
|
+
*/
|
|
55
|
+
voc: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Common handling for a device measuring air quality.
|
|
59
|
+
*/
|
|
60
|
+
export interface iAirQualitySensor extends iJsonOmitKeys {
|
|
61
|
+
/**
|
|
62
|
+
* The most recent readings of this sensor
|
|
63
|
+
*/
|
|
64
|
+
readonly readings: iAirQualityReadings;
|
|
65
|
+
/**
|
|
66
|
+
* The timestamp in ms of the last received reading
|
|
67
|
+
*/
|
|
68
|
+
lastSeen: number;
|
|
69
|
+
/**
|
|
70
|
+
* Applies new readings, firing the change callbacks if at least one value actually changed.
|
|
71
|
+
* @param readings - The metrics reported by the device, omitting the ones it does not measure
|
|
72
|
+
*/
|
|
73
|
+
update(readings: Partial<iAirQualityReadings>): void;
|
|
74
|
+
/**
|
|
75
|
+
* Whether the device actually measures the given metric.
|
|
76
|
+
* @param metric - The metric to check
|
|
77
|
+
* @returns True if the device has reported a value for it
|
|
78
|
+
*/
|
|
79
|
+
reports(metric: keyof iAirQualityReadings): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Persists the current air quality information to the database
|
|
82
|
+
*/
|
|
83
|
+
persist(): void;
|
|
84
|
+
/**
|
|
85
|
+
* Adds a callback to be called when any of the readings change
|
|
86
|
+
* @param pCallback - The callback to be called
|
|
87
|
+
*/
|
|
88
|
+
addAirQualityCallback(pCallback: (action: AirQualitySensorChangeAction) => void): void;
|
|
89
|
+
/**
|
|
90
|
+
* Frees up the resources of this sensor
|
|
91
|
+
*/
|
|
92
|
+
dispose(): void;
|
|
93
|
+
/**
|
|
94
|
+
* @returns The serializable representation of this sensor
|
|
95
|
+
*/
|
|
96
|
+
toJSON(): Partial<iAirQualitySensor>;
|
|
97
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UNDEFINED_AIR_QUALITY_VALUE = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The value reported for a metric the sensor does not measure or has not reported yet.
|
|
6
|
+
*/
|
|
7
|
+
exports.UNDEFINED_AIR_QUALITY_VALUE = -1;
|
|
@@ -12,6 +12,8 @@ export * from './iExcessEnergyConsumer';
|
|
|
12
12
|
export * from './iGarageDoorOpener';
|
|
13
13
|
export * from './iHandle';
|
|
14
14
|
export * from './iHeater';
|
|
15
|
+
export * from './iAirQualityCollector';
|
|
16
|
+
export * from './iAirQualitySensor';
|
|
15
17
|
export * from './iHumidityCollector';
|
|
16
18
|
export * from './iIlluminationSensor';
|
|
17
19
|
export * from './iLamp';
|
|
@@ -27,6 +27,8 @@ __exportStar(require("./iExcessEnergyConsumer"), exports);
|
|
|
27
27
|
__exportStar(require("./iGarageDoorOpener"), exports);
|
|
28
28
|
__exportStar(require("./iHandle"), exports);
|
|
29
29
|
__exportStar(require("./iHeater"), exports);
|
|
30
|
+
__exportStar(require("./iAirQualityCollector"), exports);
|
|
31
|
+
__exportStar(require("./iAirQualitySensor"), exports);
|
|
30
32
|
__exportStar(require("./iHumidityCollector"), exports);
|
|
31
33
|
__exportStar(require("./iIlluminationSensor"), exports);
|
|
32
34
|
__exportStar(require("./iLamp"), exports);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { iAcDevice, iActuator, iBaseDevice, iBatteryDevice, iButtonSwitch, iHandle, iHeater, iHumidityCollector, iIlluminationSensor, iMotionSensor, iShutter, iTemperatureCollector, iZigbeeDevice } from './baseDevices';
|
|
1
|
+
import { iAcDevice, iActuator, iAirQualityCollector, iBaseDevice, iBatteryDevice, iButtonSwitch, iHandle, iHeater, iHumidityCollector, iIlluminationSensor, iMotionSensor, iShutter, iTemperatureCollector, iZigbeeDevice } from './baseDevices';
|
|
2
2
|
import { iTemperatureMeasurement } from './iTemperatureMeasurement';
|
|
3
3
|
import { iRoomBase } from './iRoomBase';
|
|
4
4
|
import { ButtonPressType } from '../enums';
|
|
@@ -113,6 +113,11 @@ export interface iPersist {
|
|
|
113
113
|
* @param device - The device to persist data for
|
|
114
114
|
*/
|
|
115
115
|
persistHumiditySensor(device: iHumidityCollector): void;
|
|
116
|
+
/**
|
|
117
|
+
* Persists data of an air quality sensor
|
|
118
|
+
* @param device - The device to persist data for
|
|
119
|
+
*/
|
|
120
|
+
persistAirQualitySensor(device: iAirQualityCollector): void;
|
|
116
121
|
/**
|
|
117
122
|
* Persists data of a handle sensor
|
|
118
123
|
* @param device - The device to persist data for
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PoolConfig } from 'pg';
|
|
2
|
-
import { iAcDevice, iActuator, iBaseDevice, iBatteryDevice, iButtonSwitch, iDesiredShutterPosition, iHandle, iHeater, iHumidityCollector, iIlluminationSensor, iMotionSensor, iPersist, iRoomBase, iShutter, iShutterCalibration, iTemperatureCollector, iTemperatureMeasurement, iZigbeeDevice } from '../../interfaces';
|
|
2
|
+
import { iAcDevice, iActuator, iBaseDevice, iBatteryDevice, iButtonSwitch, iDesiredShutterPosition, iHandle, iHeater, iAirQualityCollector, iHumidityCollector, iIlluminationSensor, iMotionSensor, iPersist, iRoomBase, iShutter, iShutterCalibration, iTemperatureCollector, iTemperatureMeasurement, iZigbeeDevice } from '../../interfaces';
|
|
3
3
|
import { CountToday, EnergyCalculation } from '../../models';
|
|
4
4
|
import { ButtonPressType } from '../../enums';
|
|
5
5
|
export declare class PostgreSqlPersist implements iPersist {
|
|
@@ -42,6 +42,8 @@ export declare class PostgreSqlPersist implements iPersist {
|
|
|
42
42
|
/** @inheritDoc */
|
|
43
43
|
persistHumiditySensor(device: iHumidityCollector): void;
|
|
44
44
|
/** @inheritDoc */
|
|
45
|
+
persistAirQualitySensor(device: iAirQualityCollector): void;
|
|
46
|
+
/** @inheritDoc */
|
|
45
47
|
persistBatteryDevice(device: iBatteryDevice): void;
|
|
46
48
|
/** @inheritDoc */
|
|
47
49
|
persistZigbeeDevice(device: iZigbeeDevice): void;
|