matterbridge-dyson-robot 0.1.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/CHANGELOG.md +15 -0
- package/LICENSE +14 -0
- package/README.md +365 -0
- package/dist/async-eventemitter.d.ts +9 -0
- package/dist/async-eventemitter.d.ts.map +1 -0
- package/dist/async-eventemitter.js +35 -0
- package/dist/async-eventemitter.js.map +1 -0
- package/dist/check-configuration.d.ts +5 -0
- package/dist/check-configuration.d.ts.map +1 -0
- package/dist/check-configuration.js +35 -0
- package/dist/check-configuration.js.map +1 -0
- package/dist/check-versions.d.ts +3 -0
- package/dist/check-versions.d.ts.map +1 -0
- package/dist/check-versions.js +31 -0
- package/dist/check-versions.js.map +1 -0
- package/dist/config-types.d.ts +48 -0
- package/dist/config-types.d.ts.map +1 -0
- package/dist/config-types.js +4 -0
- package/dist/config-types.js.map +1 -0
- package/dist/decorator-changed.d.ts +16 -0
- package/dist/decorator-changed.d.ts.map +1 -0
- package/dist/decorator-changed.js +62 -0
- package/dist/decorator-changed.js.map +1 -0
- package/dist/dyson-360-msg-types.d.ts +113 -0
- package/dist/dyson-360-msg-types.d.ts.map +1 -0
- package/dist/dyson-360-msg-types.js +4 -0
- package/dist/dyson-360-msg-types.js.map +1 -0
- package/dist/dyson-360-types.d.ts +76 -0
- package/dist/dyson-360-types.d.ts.map +1 -0
- package/dist/dyson-360-types.js +72 -0
- package/dist/dyson-360-types.js.map +1 -0
- package/dist/dyson-air-msg-types.d.ts +99 -0
- package/dist/dyson-air-msg-types.d.ts.map +1 -0
- package/dist/dyson-air-msg-types.js +4 -0
- package/dist/dyson-air-msg-types.js.map +1 -0
- package/dist/dyson-air-sensor-types.d.ts +59 -0
- package/dist/dyson-air-sensor-types.d.ts.map +1 -0
- package/dist/dyson-air-sensor-types.js +4 -0
- package/dist/dyson-air-sensor-types.js.map +1 -0
- package/dist/dyson-air-state-types.d.ts +96 -0
- package/dist/dyson-air-state-types.d.ts.map +1 -0
- package/dist/dyson-air-state-types.js +4 -0
- package/dist/dyson-air-state-types.js.map +1 -0
- package/dist/dyson-air-types.d.ts +325 -0
- package/dist/dyson-air-types.d.ts.map +1 -0
- package/dist/dyson-air-types.js +382 -0
- package/dist/dyson-air-types.js.map +1 -0
- package/dist/dyson-device-360-base.d.ts +35 -0
- package/dist/dyson-device-360-base.d.ts.map +1 -0
- package/dist/dyson-device-360-base.js +233 -0
- package/dist/dyson-device-360-base.js.map +1 -0
- package/dist/dyson-device-360-commands.d.ts +7 -0
- package/dist/dyson-device-360-commands.d.ts.map +1 -0
- package/dist/dyson-device-360-commands.js +129 -0
- package/dist/dyson-device-360-commands.js.map +1 -0
- package/dist/dyson-device-360-faults-table.d.ts +20 -0
- package/dist/dyson-device-360-faults-table.d.ts.map +1 -0
- package/dist/dyson-device-360-faults-table.js +161 -0
- package/dist/dyson-device-360-faults-table.js.map +1 -0
- package/dist/dyson-device-360-faults.d.ts +10 -0
- package/dist/dyson-device-360-faults.d.ts.map +1 -0
- package/dist/dyson-device-360-faults.js +123 -0
- package/dist/dyson-device-360-faults.js.map +1 -0
- package/dist/dyson-device-360.d.ts +38 -0
- package/dist/dyson-device-360.d.ts.map +1 -0
- package/dist/dyson-device-360.js +45 -0
- package/dist/dyson-device-360.js.map +1 -0
- package/dist/dyson-device-air-base.d.ts +41 -0
- package/dist/dyson-device-air-base.d.ts.map +1 -0
- package/dist/dyson-device-air-base.js +446 -0
- package/dist/dyson-device-air-base.js.map +1 -0
- package/dist/dyson-device-air-heat.d.ts +52 -0
- package/dist/dyson-device-air-heat.d.ts.map +1 -0
- package/dist/dyson-device-air-heat.js +71 -0
- package/dist/dyson-device-air-heat.js.map +1 -0
- package/dist/dyson-device-air-quality.d.ts +7 -0
- package/dist/dyson-device-air-quality.d.ts.map +1 -0
- package/dist/dyson-device-air-quality.js +101 -0
- package/dist/dyson-device-air-quality.js.map +1 -0
- package/dist/dyson-device-air.d.ts +216 -0
- package/dist/dyson-device-air.d.ts.map +1 -0
- package/dist/dyson-device-air.js +80 -0
- package/dist/dyson-device-air.js.map +1 -0
- package/dist/dyson-device-base.d.ts +49 -0
- package/dist/dyson-device-base.d.ts.map +1 -0
- package/dist/dyson-device-base.js +46 -0
- package/dist/dyson-device-base.js.map +1 -0
- package/dist/dyson-device.d.ts +5 -0
- package/dist/dyson-device.d.ts.map +1 -0
- package/dist/dyson-device.js +43 -0
- package/dist/dyson-device.js.map +1 -0
- package/dist/dyson-mqtt-360.d.ts +16 -0
- package/dist/dyson-mqtt-360.d.ts.map +1 -0
- package/dist/dyson-mqtt-360.js +83 -0
- package/dist/dyson-mqtt-360.js.map +1 -0
- package/dist/dyson-mqtt-air.d.ts +48 -0
- package/dist/dyson-mqtt-air.d.ts.map +1 -0
- package/dist/dyson-mqtt-air.js +216 -0
- package/dist/dyson-mqtt-air.js.map +1 -0
- package/dist/dyson-mqtt-config.d.ts +6 -0
- package/dist/dyson-mqtt-config.d.ts.map +1 -0
- package/dist/dyson-mqtt-config.js +47 -0
- package/dist/dyson-mqtt-config.js.map +1 -0
- package/dist/dyson-mqtt-connect.d.ts +22 -0
- package/dist/dyson-mqtt-connect.d.ts.map +1 -0
- package/dist/dyson-mqtt-connect.js +145 -0
- package/dist/dyson-mqtt-connect.js.map +1 -0
- package/dist/dyson-mqtt-filter.d.ts +10 -0
- package/dist/dyson-mqtt-filter.d.ts.map +1 -0
- package/dist/dyson-mqtt-filter.js +25 -0
- package/dist/dyson-mqtt-filter.js.map +1 -0
- package/dist/dyson-mqtt-parse.d.ts +16 -0
- package/dist/dyson-mqtt-parse.d.ts.map +1 -0
- package/dist/dyson-mqtt-parse.js +100 -0
- package/dist/dyson-mqtt-parse.js.map +1 -0
- package/dist/dyson-mqtt-subscribe.d.ts +28 -0
- package/dist/dyson-mqtt-subscribe.d.ts.map +1 -0
- package/dist/dyson-mqtt-subscribe.js +85 -0
- package/dist/dyson-mqtt-subscribe.js.map +1 -0
- package/dist/dyson-mqtt.d.ts +51 -0
- package/dist/dyson-mqtt.d.ts.map +1 -0
- package/dist/dyson-mqtt.js +138 -0
- package/dist/dyson-mqtt.js.map +1 -0
- package/dist/dyson-types.d.ts +18 -0
- package/dist/dyson-types.d.ts.map +1 -0
- package/dist/dyson-types.js +20 -0
- package/dist/dyson-types.js.map +1 -0
- package/dist/endpoint-360-behavior.d.ts +55 -0
- package/dist/endpoint-360-behavior.d.ts.map +1 -0
- package/dist/endpoint-360-behavior.js +156 -0
- package/dist/endpoint-360-behavior.js.map +1 -0
- package/dist/endpoint-360-rvc.d.ts +14 -0
- package/dist/endpoint-360-rvc.d.ts.map +1 -0
- package/dist/endpoint-360-rvc.js +149 -0
- package/dist/endpoint-360-rvc.js.map +1 -0
- package/dist/endpoint-360.d.ts +35 -0
- package/dist/endpoint-360.d.ts.map +1 -0
- package/dist/endpoint-360.js +197 -0
- package/dist/endpoint-360.js.map +1 -0
- package/dist/endpoint-air-purifier.d.ts +24 -0
- package/dist/endpoint-air-purifier.d.ts.map +1 -0
- package/dist/endpoint-air-purifier.js +97 -0
- package/dist/endpoint-air-purifier.js.map +1 -0
- package/dist/endpoint-air-quality.d.ts +11 -0
- package/dist/endpoint-air-quality.d.ts.map +1 -0
- package/dist/endpoint-air-quality.js +104 -0
- package/dist/endpoint-air-quality.js.map +1 -0
- package/dist/endpoint-air-thermostat.d.ts +358 -0
- package/dist/endpoint-air-thermostat.d.ts.map +1 -0
- package/dist/endpoint-air-thermostat.js +67 -0
- package/dist/endpoint-air-thermostat.js.map +1 -0
- package/dist/endpoint-air.d.ts +125 -0
- package/dist/endpoint-air.d.ts.map +1 -0
- package/dist/endpoint-air.js +459 -0
- package/dist/endpoint-air.js.map +1 -0
- package/dist/endpoint-base.d.ts +36 -0
- package/dist/endpoint-base.d.ts.map +1 -0
- package/dist/endpoint-base.js +137 -0
- package/dist/endpoint-base.js.map +1 -0
- package/dist/error-360.d.ts +35 -0
- package/dist/error-360.d.ts.map +1 -0
- package/dist/error-360.js +105 -0
- package/dist/error-360.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/logger-filter.d.ts +23 -0
- package/dist/logger-filter.d.ts.map +1 -0
- package/dist/logger-filter.js +104 -0
- package/dist/logger-filter.js.map +1 -0
- package/dist/logger-options.d.ts +18 -0
- package/dist/logger-options.d.ts.map +1 -0
- package/dist/logger-options.js +41 -0
- package/dist/logger-options.js.map +1 -0
- package/dist/logger-prefix.d.ts +12 -0
- package/dist/logger-prefix.d.ts.map +1 -0
- package/dist/logger-prefix.js +37 -0
- package/dist/logger-prefix.js.map +1 -0
- package/dist/periodic.d.ts +31 -0
- package/dist/periodic.d.ts.map +1 -0
- package/dist/periodic.js +102 -0
- package/dist/periodic.js.map +1 -0
- package/dist/platform.d.ts +18 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +138 -0
- package/dist/platform.js.map +1 -0
- package/dist/settings.d.ts +9 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +28 -0
- package/dist/settings.js.map +1 -0
- package/dist/ti/config-types-ti.d.ts +16 -0
- package/dist/ti/config-types-ti.d.ts.map +1 -0
- package/dist/ti/config-types-ti.js +65 -0
- package/dist/ti/config-types-ti.js.map +1 -0
- package/dist/ti/config-types.d.ts +37 -0
- package/dist/ti/config-types.d.ts.map +1 -0
- package/dist/ti/config-types.js +11 -0
- package/dist/ti/config-types.js.map +1 -0
- package/dist/ti/dyson-360-msg-types-ti.d.ts +22 -0
- package/dist/ti/dyson-360-msg-types-ti.d.ts.map +1 -0
- package/dist/ti/dyson-360-msg-types-ti.js +134 -0
- package/dist/ti/dyson-360-msg-types-ti.js.map +1 -0
- package/dist/ti/dyson-360-msg-types.d.ts +55 -0
- package/dist/ti/dyson-360-msg-types.d.ts.map +1 -0
- package/dist/ti/dyson-360-msg-types.js +13 -0
- package/dist/ti/dyson-360-msg-types.js.map +1 -0
- package/dist/ti/dyson-360-types-ti.d.ts +17 -0
- package/dist/ti/dyson-360-types-ti.d.ts.map +1 -0
- package/dist/ti/dyson-360-types-ti.js +94 -0
- package/dist/ti/dyson-360-types-ti.js.map +1 -0
- package/dist/ti/dyson-360-types.d.ts +40 -0
- package/dist/ti/dyson-360-types.d.ts.map +1 -0
- package/dist/ti/dyson-360-types.js +11 -0
- package/dist/ti/dyson-360-types.js.map +1 -0
- package/dist/ti/dyson-air-msg-types-ti.d.ts +19 -0
- package/dist/ti/dyson-air-msg-types-ti.d.ts.map +1 -0
- package/dist/ti/dyson-air-msg-types-ti.js +115 -0
- package/dist/ti/dyson-air-msg-types-ti.js.map +1 -0
- package/dist/ti/dyson-air-msg-types.d.ts +46 -0
- package/dist/ti/dyson-air-msg-types.d.ts.map +1 -0
- package/dist/ti/dyson-air-msg-types.js +15 -0
- package/dist/ti/dyson-air-msg-types.js.map +1 -0
- package/dist/ti/dyson-air-sensor-types-ti.d.ts +9 -0
- package/dist/ti/dyson-air-sensor-types-ti.d.ts.map +1 -0
- package/dist/ti/dyson-air-sensor-types-ti.js +68 -0
- package/dist/ti/dyson-air-sensor-types-ti.js.map +1 -0
- package/dist/ti/dyson-air-sensor-types.d.ts +16 -0
- package/dist/ti/dyson-air-sensor-types.d.ts.map +1 -0
- package/dist/ti/dyson-air-sensor-types.js +12 -0
- package/dist/ti/dyson-air-sensor-types.js.map +1 -0
- package/dist/ti/dyson-air-state-types-ti.d.ts +9 -0
- package/dist/ti/dyson-air-state-types-ti.d.ts.map +1 -0
- package/dist/ti/dyson-air-state-types-ti.js +105 -0
- package/dist/ti/dyson-air-state-types-ti.js.map +1 -0
- package/dist/ti/dyson-air-state-types.d.ts +16 -0
- package/dist/ti/dyson-air-state-types.d.ts.map +1 -0
- package/dist/ti/dyson-air-state-types.js +12 -0
- package/dist/ti/dyson-air-state-types.js.map +1 -0
- package/dist/ti/dyson-air-types-ti.d.ts +59 -0
- package/dist/ti/dyson-air-types-ti.d.ts.map +1 -0
- package/dist/ti/dyson-air-types-ti.js +385 -0
- package/dist/ti/dyson-air-types-ti.js.map +1 -0
- package/dist/ti/dyson-air-types.d.ts +166 -0
- package/dist/ti/dyson-air-types.d.ts.map +1 -0
- package/dist/ti/dyson-air-types.js +11 -0
- package/dist/ti/dyson-air-types.js.map +1 -0
- package/dist/ti/dyson-types-ti.d.ts +10 -0
- package/dist/ti/dyson-types-ti.d.ts.map +1 -0
- package/dist/ti/dyson-types-ti.js +29 -0
- package/dist/ti/dyson-types-ti.js.map +1 -0
- package/dist/ti/dyson-types.d.ts +19 -0
- package/dist/ti/dyson-types.d.ts.map +1 -0
- package/dist/ti/dyson-types.js +11 -0
- package/dist/ti/dyson-types.js.map +1 -0
- package/dist/utils.d.ts +24 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +150 -0
- package/dist/utils.js.map +1 -0
- package/matterbridge-dyson-robot.schema.json +359 -0
- package/package.json +93 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Matterbridge plugin for Dyson robot vacuum and air treatment devices
|
|
2
|
+
// Copyright © 2025 Alexander Thoukydides
|
|
3
|
+
import { DysonMqtt } from './dyson-mqtt.js';
|
|
4
|
+
import { checkers as dysonMsgCheckers360 } from './ti/dyson-360-msg-types.js';
|
|
5
|
+
import { tryListener } from './utils.js';
|
|
6
|
+
import { Dyson360CleaningMode, Dyson360CleaningType } from './dyson-360-types.js';
|
|
7
|
+
import { DysonModeReason } from './dyson-types.js';
|
|
8
|
+
// Configuration of a Dyson MQTT client for robot vacuums
|
|
9
|
+
const DYSON_MQTT_CONFIG_360 = {
|
|
10
|
+
topics: {
|
|
11
|
+
command: '@/@/command',
|
|
12
|
+
subscribe: ['@/@/status'],
|
|
13
|
+
other: ['@/initialconnection/credentials',
|
|
14
|
+
'@/initialconnection/status']
|
|
15
|
+
},
|
|
16
|
+
messages: {
|
|
17
|
+
prefix: 'Dyson360Msg',
|
|
18
|
+
checkers: dysonMsgCheckers360
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
// Dyson MQTT client for robot vacuums
|
|
22
|
+
export class DysonMqtt360 extends DysonMqtt {
|
|
23
|
+
// Construct a new MQTT client
|
|
24
|
+
constructor(log, config, device) {
|
|
25
|
+
super(log, config, device, DYSON_MQTT_CONFIG_360);
|
|
26
|
+
// Handle MQTT events
|
|
27
|
+
this.on('subscribed', tryListener(this, async () =>
|
|
28
|
+
// Request the current status when (re)connected
|
|
29
|
+
this.publish('REQUEST-CURRENT-STATE'))).on('message', tryListener(this, msg => {
|
|
30
|
+
// Update the robot vacuum state from the received messages
|
|
31
|
+
switch (msg.msg) {
|
|
32
|
+
case 'STATE-CHANGE':
|
|
33
|
+
msg = this.convertStateChange(msg);
|
|
34
|
+
// (fallthrough)
|
|
35
|
+
case 'CURRENT-STATE':
|
|
36
|
+
this.updateState(msg);
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
// Convert a STATE-CHANGE message to CURRENT-STATE format
|
|
42
|
+
convertStateChange(msg) {
|
|
43
|
+
const { msg: _, newstate: state, oldstate, endOfClean, ...otherFields } = msg;
|
|
44
|
+
return { msg: 'CURRENT-STATE', state, ...otherFields };
|
|
45
|
+
}
|
|
46
|
+
// Update the robot vacuum state from a received message
|
|
47
|
+
updateState(msg) {
|
|
48
|
+
// Copy most fields from the message to the state
|
|
49
|
+
const { msg: _msg, time, ...status } = msg;
|
|
50
|
+
this.status.faults = undefined; // (ensure faults get cleared)
|
|
51
|
+
Object.assign(this.status, status);
|
|
52
|
+
// State is fully initialised after the first message has been received
|
|
53
|
+
if (!this.status.initialised) {
|
|
54
|
+
this.status.initialised = true;
|
|
55
|
+
this.log.info('MQTT client initialisation complete');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Publish a robot vacuum command to perform an action
|
|
59
|
+
commandAction(msg) {
|
|
60
|
+
switch (msg) {
|
|
61
|
+
case 'START':
|
|
62
|
+
return this.publish('START', {
|
|
63
|
+
'mode-reason': DysonModeReason.LocalApp,
|
|
64
|
+
fullCleanType: Dyson360CleaningType.Immediate,
|
|
65
|
+
cleaningMode: this.status.defaultCleaningMode && Dyson360CleaningMode.Global
|
|
66
|
+
});
|
|
67
|
+
case 'PAUSE':
|
|
68
|
+
case 'RESUME':
|
|
69
|
+
case 'ABORT':
|
|
70
|
+
return this.publish(msg, {
|
|
71
|
+
'mode-reason': DysonModeReason.LocalApp
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Publish a robot vacuum command to set the default power level
|
|
76
|
+
commandPower(defaultVacuumPowerMode) {
|
|
77
|
+
return this.publish('STATE-SET', {
|
|
78
|
+
'mode-reason': DysonModeReason.LocalApp,
|
|
79
|
+
data: { defaultVacuumPowerMode }
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=dyson-mqtt-360.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dyson-mqtt-360.js","sourceRoot":"","sources":["../src/dyson-mqtt-360.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { DysonMqtt } from './dyson-mqtt.js';
|
|
2
|
+
import { TypeMap as DysonMsgMapAir } from './ti/dyson-air-msg-types.js';
|
|
3
|
+
import { Config, DeviceConfig } from './config-types.js';
|
|
4
|
+
import { AnsiLogger } from 'matterbridge/logger';
|
|
5
|
+
import { DysonAirMsgCurrentFaults, DysonAirMsgCurrentState, DysonAirMsgEnvironmentalCurrentSensorData, DysonAirMsgFaultsChange, DysonAirMsgHello, DysonAirMsgStateChange } from './dyson-air-msg-types.js';
|
|
6
|
+
import { DysonAirModuleError, DysonAirModuleWarning, DysonAirProductError, DysonAirProductWarning, DysonAirSensorValueEnum, DysonAirSleepTimerEnum } from './dyson-air-types.js';
|
|
7
|
+
import { DysonAirCurrentSensorData } from './dyson-air-sensor-types.js';
|
|
8
|
+
import { DysonAirProductState } from './dyson-air-state-types.js';
|
|
9
|
+
import { DysonMsgAny } from './dyson-mqtt-parse.js';
|
|
10
|
+
type ProductStateNumericEnumKeys = 'cflr' | 'fnsp' | 'nmdv';
|
|
11
|
+
declare const PRODUCT_STATE_NUMERIC_KEYS: readonly ["hmax", "hflr", "filf", "osal", "osau", "cdrr", "cltr", "humt", "rect"];
|
|
12
|
+
type ProductStateNumericKeys = typeof PRODUCT_STATE_NUMERIC_KEYS[number];
|
|
13
|
+
type ProductStateVerbatimKeys = Exclude<keyof DysonAirProductState, ProductStateNumericEnumKeys | ProductStateNumericKeys>;
|
|
14
|
+
export type DysonMqttProductState = Pick<DysonAirProductState, ProductStateVerbatimKeys> & {
|
|
15
|
+
[K in ProductStateNumericEnumKeys]?: DysonAirProductState[K] | number;
|
|
16
|
+
} & Partial<Record<ProductStateNumericKeys, number>>;
|
|
17
|
+
interface Faults {
|
|
18
|
+
productErrors: Set<DysonAirProductError>;
|
|
19
|
+
productWarnings: Set<DysonAirProductWarning>;
|
|
20
|
+
moduleErrors: Set<DysonAirModuleError>;
|
|
21
|
+
moduleWarnings: Set<DysonAirModuleWarning>;
|
|
22
|
+
}
|
|
23
|
+
type SensorDataV2 = 'hchr' | 'p25r' | 'p10r' | 'va10';
|
|
24
|
+
export type DysonMqttStatusAirSensor = {
|
|
25
|
+
[K in keyof Omit<DysonAirCurrentSensorData, 'sltm' | SensorDataV2>]: DysonAirSensorValueEnum | number;
|
|
26
|
+
} & {
|
|
27
|
+
sltm?: DysonAirSleepTimerEnum | number;
|
|
28
|
+
};
|
|
29
|
+
export type DysonMqttStatusAir = Pick<DysonAirMsgHello, 'version'> & DysonMqttProductState & DysonMqttStatusAirSensor & Partial<Faults>;
|
|
30
|
+
export declare class DysonMqttAir extends DysonMqtt<DysonMsgMapAir, DysonMqttStatusAir> {
|
|
31
|
+
private initialiseMsgs;
|
|
32
|
+
constructor(log: AnsiLogger, config: Config, device: DeviceConfig);
|
|
33
|
+
updateStateFromMessage(msg: DysonMsgAny<DysonMsgMapAir>): void;
|
|
34
|
+
checkIfInitialised(msg: DysonMsgAny<DysonMsgMapAir>): void;
|
|
35
|
+
updateProductInfo(msg: DysonAirMsgHello): void;
|
|
36
|
+
convertStateChange(msg: DysonAirMsgStateChange): DysonAirMsgCurrentState;
|
|
37
|
+
convertFaultsChange(msg: DysonAirMsgFaultsChange): DysonAirMsgCurrentFaults;
|
|
38
|
+
updateState(msg: DysonAirMsgCurrentState): void;
|
|
39
|
+
updateSensorData(msg: DysonAirMsgEnvironmentalCurrentSensorData): void;
|
|
40
|
+
updateFaults(msg: DysonAirMsgCurrentFaults): void;
|
|
41
|
+
commandStateSet(productState: DysonMqttProductState): Promise<void>;
|
|
42
|
+
parseNumericOrEnumValue<T extends string>(description: string, enumMap: Record<string, T>, value?: string, divisor?: number): number | T | undefined;
|
|
43
|
+
}
|
|
44
|
+
export declare function KtoC(kelvin: number): number;
|
|
45
|
+
export declare function roundedKtoC(kelvin: number): number;
|
|
46
|
+
export declare function roundedCtoK(celsius: number): number;
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=dyson-mqtt-air.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dyson-mqtt-air.d.ts","sourceRoot":"","sources":["../src/dyson-mqtt-air.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// Matterbridge plugin for Dyson robot vacuum and air treatment devices
|
|
2
|
+
// Copyright © 2025 Alexander Thoukydides
|
|
3
|
+
import { DysonMqtt } from './dyson-mqtt.js';
|
|
4
|
+
import { checkers as dysonMsgCheckersAir } from './ti/dyson-air-msg-types.js';
|
|
5
|
+
import { formatList, tryListener } from './utils.js';
|
|
6
|
+
import { DysonAirCarbonFilterEnum, DysonAirErrorCodeEnum, DysonAirFanSpeed, DysonAirFaultStatus, DysonAirModuleError, DysonAirModuleWarning, DysonAirProductError, DysonAirProductWarning, DysonAirSensorValueEnum, DysonAirSleepTimerEnum, DysonAirWarningCodeEnum } from './dyson-air-types.js';
|
|
7
|
+
import { DysonModeReason } from './dyson-types.js';
|
|
8
|
+
// Configuration of a Dyson MQTT client for robot vacuums
|
|
9
|
+
const DYSON_MQTT_CONFIG_AIR = {
|
|
10
|
+
topics: {
|
|
11
|
+
command: '@/@/command',
|
|
12
|
+
subscribe: ['@/@/status/connection',
|
|
13
|
+
'@/@/status/current',
|
|
14
|
+
'@/@/status/faults']
|
|
15
|
+
},
|
|
16
|
+
messages: {
|
|
17
|
+
prefix: 'DysonAirMsg',
|
|
18
|
+
checkers: dysonMsgCheckersAir
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const PRODUCT_STATE_NUMERIC_KEYS = ['hmax', 'hflr', 'filf', 'osal', 'osau', 'cdrr', 'cltr', 'humt', 'rect'];
|
|
22
|
+
// Dyson MQTT client for air treatment machines
|
|
23
|
+
export class DysonMqttAir extends DysonMqtt {
|
|
24
|
+
// Messages still required for initialisation
|
|
25
|
+
initialiseMsgs = new Set([
|
|
26
|
+
'HELLO',
|
|
27
|
+
'CURRENT-STATE',
|
|
28
|
+
'ENVIRONMENTAL-CURRENT-SENSOR-DATA'
|
|
29
|
+
]);
|
|
30
|
+
// Construct a new MQTT client
|
|
31
|
+
constructor(log, config, device) {
|
|
32
|
+
super(log, config, device, DYSON_MQTT_CONFIG_AIR);
|
|
33
|
+
// Handle MQTT events
|
|
34
|
+
this.on('subscribed', tryListener(this, async () => {
|
|
35
|
+
// Request the current status when (re)connected
|
|
36
|
+
await this.publish('REQUEST-CURRENT-STATE');
|
|
37
|
+
await this.publish('REQUEST-PRODUCT-ENVIRONMENT-CURRENT-SENSOR-DATA');
|
|
38
|
+
})).on('message', tryListener(this, msg => {
|
|
39
|
+
// Update the robot vacuum state from the received messages
|
|
40
|
+
this.updateStateFromMessage(msg);
|
|
41
|
+
this.checkIfInitialised(msg);
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
// Update state from a received message
|
|
45
|
+
updateStateFromMessage(msg) {
|
|
46
|
+
switch (msg.msg) {
|
|
47
|
+
case 'HELLO':
|
|
48
|
+
this.updateProductInfo(msg);
|
|
49
|
+
break;
|
|
50
|
+
case 'STATE-CHANGE':
|
|
51
|
+
msg = this.convertStateChange(msg);
|
|
52
|
+
// (fallthrough)
|
|
53
|
+
case 'CURRENT-STATE':
|
|
54
|
+
this.updateState(msg);
|
|
55
|
+
break;
|
|
56
|
+
case 'ENVIRONMENTAL-CURRENT-SENSOR-DATA':
|
|
57
|
+
this.updateSensorData(msg);
|
|
58
|
+
break;
|
|
59
|
+
case 'FAULTS-CHANGE':
|
|
60
|
+
msg = this.convertFaultsChange(msg);
|
|
61
|
+
// (fallthrough)
|
|
62
|
+
case 'CURRENT-FAULTS':
|
|
63
|
+
this.updateFaults(msg);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Check whether all required messages have been received
|
|
67
|
+
checkIfInitialised(msg) {
|
|
68
|
+
this.initialiseMsgs.delete(msg.msg);
|
|
69
|
+
if (!this.status.initialised && this.initialiseMsgs.size === 0) {
|
|
70
|
+
this.status.initialised = true;
|
|
71
|
+
this.log.info('MQTT client initialisation complete');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Update hardware and software state from a received message
|
|
75
|
+
updateProductInfo(msg) {
|
|
76
|
+
this.status.version = msg.version;
|
|
77
|
+
}
|
|
78
|
+
// Convert a STATE-CHANGE message to CURRENT-STATE format
|
|
79
|
+
convertStateChange(msg) {
|
|
80
|
+
const productState = convertChangesToStatus(msg.productState);
|
|
81
|
+
return { ...msg, msg: 'CURRENT-STATE', productState };
|
|
82
|
+
}
|
|
83
|
+
// Convert a FAULTS-CHANGE message to CURRENT-FAULTS format
|
|
84
|
+
convertFaultsChange(msg) {
|
|
85
|
+
return {
|
|
86
|
+
msg: 'CURRENT-FAULTS',
|
|
87
|
+
time: msg.time,
|
|
88
|
+
productErrors: convertChangesToStatus(msg.productErrors),
|
|
89
|
+
productWarnings: convertChangesToStatus(msg.productWarnings),
|
|
90
|
+
moduleErrors: convertChangesToStatus(msg.moduleErrors),
|
|
91
|
+
moduleWarnings: convertChangesToStatus(msg.moduleWarnings)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// Update product state from a received message
|
|
95
|
+
updateState(msg) {
|
|
96
|
+
// Check whether the error and warning codes are known
|
|
97
|
+
const checkCode = (description, key, knownValues) => {
|
|
98
|
+
const value = msg.productState[key];
|
|
99
|
+
if (value === undefined || Object.values(knownValues).includes(value))
|
|
100
|
+
return;
|
|
101
|
+
this.log.warn(`Received unknown ${description}: ${value}`);
|
|
102
|
+
};
|
|
103
|
+
checkCode('error code', 'ercd', DysonAirErrorCodeEnum);
|
|
104
|
+
checkCode('warning code', 'wacd', DysonAirWarningCodeEnum);
|
|
105
|
+
// Copy everything initially, but some values will be overwritten
|
|
106
|
+
const { productState } = msg;
|
|
107
|
+
Object.assign(this.status, productState);
|
|
108
|
+
// Parse values that can be either numeric or enum values
|
|
109
|
+
this.status.cflr = this.parseNumericOrEnumValue('cflr', DysonAirCarbonFilterEnum, productState.cflr);
|
|
110
|
+
this.status.fnsp = this.parseNumericOrEnumValue('fnsp', DysonAirFanSpeed, productState.fnsp);
|
|
111
|
+
this.status.nmdv = this.parseNumericOrEnumValue('nmdv', DysonAirFanSpeed, productState.nmdv);
|
|
112
|
+
// Parse values that should always be numeric strings
|
|
113
|
+
for (const key of PRODUCT_STATE_NUMERIC_KEYS) {
|
|
114
|
+
const value = productState[key];
|
|
115
|
+
this.status[key] = value === undefined ? undefined : Number(value);
|
|
116
|
+
}
|
|
117
|
+
// Convert target temperature from Kelvin to Celsius
|
|
118
|
+
if (this.status.hmax)
|
|
119
|
+
this.status.hmax = roundedKtoC(this.status.hmax / 10);
|
|
120
|
+
}
|
|
121
|
+
// Update environmental sensor data from a received message
|
|
122
|
+
updateSensorData(msg) {
|
|
123
|
+
const parse = (field, divisor) => this.parseNumericOrEnumValue(field, DysonAirSensorValueEnum, msg.data[field], divisor);
|
|
124
|
+
// Convert the sensor data to numeric form with appropriate scaling
|
|
125
|
+
this.status.hact = parse('hact');
|
|
126
|
+
this.status.co2r = parse('co2r');
|
|
127
|
+
this.status.pact = parse('pact');
|
|
128
|
+
this.status.hcho = parse('hchr') ?? parse('hcho');
|
|
129
|
+
this.status.noxl = parse('noxl');
|
|
130
|
+
this.status.pm25 = parse('p25r') ?? parse('pm25');
|
|
131
|
+
this.status.pm10 = parse('p10r') ?? parse('pm10');
|
|
132
|
+
this.status.vact = parse('va10') ?? parse('vact', 1 / 11);
|
|
133
|
+
// Convert temperature from Kelvin to Celsius
|
|
134
|
+
const kelvin = parse('tact', 10);
|
|
135
|
+
this.status.tact = typeof kelvin === 'number' ? KtoC(kelvin) : kelvin;
|
|
136
|
+
// Similarly for the sleep timer
|
|
137
|
+
this.status.sltm = this.parseNumericOrEnumValue('sltm', DysonAirSleepTimerEnum, msg.data.sltm);
|
|
138
|
+
}
|
|
139
|
+
// Update environmental sensor data from a received message
|
|
140
|
+
updateFaults(msg) {
|
|
141
|
+
const faultKeysCheckers = [
|
|
142
|
+
['productErrors', DysonAirProductError],
|
|
143
|
+
['productWarnings', DysonAirProductWarning],
|
|
144
|
+
['moduleErrors', DysonAirModuleError],
|
|
145
|
+
['moduleWarnings', DysonAirModuleWarning]
|
|
146
|
+
];
|
|
147
|
+
// Convert each fault type to a set of active fault codes
|
|
148
|
+
for (const [key, knownValues] of faultKeysCheckers) {
|
|
149
|
+
// Identify unknown faults and active known faults
|
|
150
|
+
const activeFaults = new Set();
|
|
151
|
+
const unknownFaults = new Set();
|
|
152
|
+
for (const [fault, status] of Object.entries(msg[key])) {
|
|
153
|
+
if (!Object.values(knownValues).includes(fault))
|
|
154
|
+
unknownFaults.add(fault);
|
|
155
|
+
else if (status === DysonAirFaultStatus.Fail)
|
|
156
|
+
activeFaults.add(fault);
|
|
157
|
+
}
|
|
158
|
+
// Log warnings for unknown faults (both active and inactive)
|
|
159
|
+
if (unknownFaults.size) {
|
|
160
|
+
this.log.warn(`Received unknown ${key}: ${formatList([...unknownFaults])}`);
|
|
161
|
+
}
|
|
162
|
+
// Update the status with the set of active faults
|
|
163
|
+
this.status[key] = activeFaults;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Publish an air treatment machine command to set the product state
|
|
167
|
+
commandStateSet(productState) {
|
|
168
|
+
// Convert values to the format required in the MQTT message
|
|
169
|
+
const data = {};
|
|
170
|
+
const mapEntry = ([key, value]) => {
|
|
171
|
+
if (value === undefined)
|
|
172
|
+
return;
|
|
173
|
+
let valueString;
|
|
174
|
+
if (typeof value === 'number') {
|
|
175
|
+
// Convert numeric values to four digit strings for the command
|
|
176
|
+
const numericValue = key === 'hmax' ? roundedCtoK(value) * 10 : value;
|
|
177
|
+
valueString = numericValue.toFixed(0).padStart(4, '0');
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
// Enum or general string values are already the correct type
|
|
181
|
+
valueString = value;
|
|
182
|
+
}
|
|
183
|
+
data[key] = valueString;
|
|
184
|
+
};
|
|
185
|
+
const entries = Object.entries(productState);
|
|
186
|
+
entries.forEach(mapEntry);
|
|
187
|
+
// Publish the command
|
|
188
|
+
return this.publish('STATE-SET', {
|
|
189
|
+
'mode-reason': DysonModeReason.LocalApp,
|
|
190
|
+
data
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
// Parse strings that can be numeric or enum values, returning the number or enum value
|
|
194
|
+
parseNumericOrEnumValue(description, enumMap, value, divisor = 1) {
|
|
195
|
+
if (value === undefined || value === '')
|
|
196
|
+
return;
|
|
197
|
+
// Try parsing as a decimal natural number
|
|
198
|
+
if (/^\d+$/.test(value))
|
|
199
|
+
return Number(value) / divisor;
|
|
200
|
+
// Otherwise check if it is a member of the specified enum type
|
|
201
|
+
const expectedValues = Object.values(enumMap);
|
|
202
|
+
if (expectedValues.includes(value))
|
|
203
|
+
return value;
|
|
204
|
+
this.log.warn(`Received unexpected '${description}' value: ${value}`
|
|
205
|
+
+ ` (expected ${expectedValues.join(', ')}, or a numeric string)`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function convertChangesToStatus(changes) {
|
|
209
|
+
return Object.fromEntries(Object.keys(changes).map((key) => [key, changes[key]?.[1]]));
|
|
210
|
+
}
|
|
211
|
+
// Temperature conversion (accurate version for 'tact')
|
|
212
|
+
export function KtoC(kelvin) { return kelvin - 273.15; }
|
|
213
|
+
// Temperature conversion (rounded version for 'hmax')
|
|
214
|
+
export function roundedKtoC(kelvin) { return kelvin - 273; }
|
|
215
|
+
export function roundedCtoK(celsius) { return celsius + 273; }
|
|
216
|
+
//# sourceMappingURL=dyson-mqtt-air.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dyson-mqtt-air.js","sourceRoot":"","sources":["../src/dyson-mqtt-air.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { DeviceConfig, DeviceConfigAny, DeviceConfigIoT, DeviceConfigMqtt, DeviceConfigWiFi } from './config-types.js';
|
|
2
|
+
export declare function isConfigWiFi(config: DeviceConfigAny): config is DeviceConfigWiFi;
|
|
3
|
+
export declare function isConfigMqtt(config: DeviceConfigAny): config is DeviceConfigMqtt;
|
|
4
|
+
export declare function isConfigIoT(config: DeviceConfigAny): config is DeviceConfigIoT;
|
|
5
|
+
export declare function getDeviceConfigMqtt(config: DeviceConfigAny): DeviceConfig;
|
|
6
|
+
//# sourceMappingURL=dyson-mqtt-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dyson-mqtt-config.d.ts","sourceRoot":"","sources":["../src/dyson-mqtt-config.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,YAAY,EACZ,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EACnB,MAAM,mBAAmB,CAAC;AAc3B,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,IAAI,gBAAgB,CAEhF;AACD,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,IAAI,gBAAgB,CAEhF;AACD,wBAAgB,WAAW,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,IAAI,eAAe,CAE9E;AAGD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,GAAG,YAAY,CAMzE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Matterbridge plugin for Dyson robot vacuum and air treatment devices
|
|
2
|
+
// Copyright © 2025 Alexander Thoukydides
|
|
3
|
+
import { createHash } from 'crypto';
|
|
4
|
+
import { assertIsDefined } from './utils.js';
|
|
5
|
+
// Regular expressions to parse Wi-Fi setup SSIDs
|
|
6
|
+
const SSID_360EYE_RE = /^(360EYE-)?(?<sn>[A-Z0-9]{3}-[A-Z]{2}-[A-Z0-9]{8,})/;
|
|
7
|
+
const SSID_OTHER_RE = /^DYSON-(?<sn>[A-Z0-9]{3}-[A-Z]{2}-[A-Z0-9]{8,})-(?<type>[0-9]{3}[A-Z]?)$/;
|
|
8
|
+
// Dyson 360 Eye uses a different Wi-Fi setup SSID format
|
|
9
|
+
const TYPE_360EYE = 'N223';
|
|
10
|
+
// Dyson Pure Hot+Cool Link (HP02) SSIDs don't match the MQTT topic and username
|
|
11
|
+
const TYPE_MAP = new Map([['455A', '455']]);
|
|
12
|
+
// Identify the type of credentials provided
|
|
13
|
+
export function isConfigWiFi(config) {
|
|
14
|
+
return 'wifi_ssid' in config;
|
|
15
|
+
}
|
|
16
|
+
export function isConfigMqtt(config) {
|
|
17
|
+
return 'password' in config;
|
|
18
|
+
}
|
|
19
|
+
export function isConfigIoT(config) {
|
|
20
|
+
return 'endpoint' in config;
|
|
21
|
+
}
|
|
22
|
+
// Convert Wi-Fi setup credentials to local MQTT credentials
|
|
23
|
+
export function getDeviceConfigMqtt(config) {
|
|
24
|
+
if (isConfigMqtt(config) || isConfigIoT(config))
|
|
25
|
+
return config;
|
|
26
|
+
const { wifi_ssid, wifi_password } = config;
|
|
27
|
+
const { root_topic, username } = parseSSID(wifi_ssid);
|
|
28
|
+
const password = hashWifiPassword(wifi_password);
|
|
29
|
+
return { ...config, username, password, root_topic };
|
|
30
|
+
}
|
|
31
|
+
// Extract the MQTT topic and username from a Wi-Fi setup SSID
|
|
32
|
+
function parseSSID(ssid) {
|
|
33
|
+
const match = SSID_360EYE_RE.exec(ssid) ?? SSID_OTHER_RE.exec(ssid);
|
|
34
|
+
if (!match)
|
|
35
|
+
throw new Error(`Unable to parse Product SSID: ${ssid}`);
|
|
36
|
+
const root_topic = match.groups?.type ?? TYPE_360EYE;
|
|
37
|
+
const serialNumber = match.groups?.sn;
|
|
38
|
+
assertIsDefined(serialNumber);
|
|
39
|
+
const username = TYPE_MAP.get(serialNumber) ?? serialNumber;
|
|
40
|
+
return { root_topic, username };
|
|
41
|
+
}
|
|
42
|
+
// Convert a Wi-Fi password to the form required for MQTT
|
|
43
|
+
function hashWifiPassword(password) {
|
|
44
|
+
const sha512 = createHash('sha512').update(password).digest();
|
|
45
|
+
return sha512.toString('base64');
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=dyson-mqtt-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dyson-mqtt-config.js","sourceRoot":"","sources":["../src/dyson-mqtt-config.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,yCAAyC;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAQpC,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,iDAAiD;AACjD,MAAM,cAAc,GAAG,qDAAqD,CAAC;AAC7E,MAAM,aAAa,GAAI,0EAA0E,CAAC;AAElG,yDAAyD;AACzD,MAAM,WAAW,GAAG,MAAM,CAAC;AAE3B,gFAAgF;AAChF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAiB,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;AAE5D,4CAA4C;AAC5C,MAAM,UAAU,YAAY,CAAC,MAAuB;IAChD,OAAO,WAAW,IAAI,MAAM,CAAC;AACjC,CAAC;AACD,MAAM,UAAU,YAAY,CAAC,MAAuB;IAChD,OAAO,UAAU,IAAI,MAAM,CAAC;AAChC,CAAC;AACD,MAAM,UAAU,WAAW,CAAC,MAAuB;IAC/C,OAAO,UAAU,IAAI,MAAM,CAAC;AAChC,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,mBAAmB,CAAC,MAAuB;IACvD,IAAI,YAAY,CAAC,MAAM,CAAC,IAAI,WAAW,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAC/D,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC;IAC5C,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,gBAAgB,CAAC,aAAa,CAAC,CAAC;IACjD,OAAO,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AACzD,CAAC;AAED,8DAA8D;AAC9D,SAAS,SAAS,CAAC,IAAY;IAC3B,MAAM,KAAK,GAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrE,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,IAAI,EAAE,CAAC,CAAC;IACrE,MAAM,UAAU,GAAM,KAAK,CAAC,MAAM,EAAE,IAAI,IAAI,WAAW,CAAC;IACxD,MAAM,YAAY,GAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;IACvC,eAAe,CAAC,YAAY,CAAC,CAAC;IAC9B,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,YAAY,CAAC;IAC5D,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AACpC,CAAC;AAED,yDAAyD;AACzD,SAAS,gBAAgB,CAAC,QAAgB;IACtC,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC;IAC9D,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AnsiLogger } from 'matterbridge/logger';
|
|
2
|
+
import { IClientOptions, MqttClient } from 'mqtt';
|
|
3
|
+
import { Config, DeviceConfig, DeviceConfigIoT, DeviceConfigMqtt } from './config-types.js';
|
|
4
|
+
export declare abstract class DysonMqttConnection<T extends DeviceConfig = DeviceConfig> {
|
|
5
|
+
readonly log: AnsiLogger;
|
|
6
|
+
readonly config: Config;
|
|
7
|
+
readonly deviceConfig: T;
|
|
8
|
+
readonly mqtt: MqttClient;
|
|
9
|
+
terminate: AbortController;
|
|
10
|
+
backoff: number;
|
|
11
|
+
uptimeStart?: number;
|
|
12
|
+
constructor(log: AnsiLogger, config: Config, deviceConfig: T, brokerUrl: string, extraOptions: IClientOptions);
|
|
13
|
+
reconnect(): Promise<void>;
|
|
14
|
+
stop(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export declare class DysonMqttConnectionLocal extends DysonMqttConnection<DeviceConfigMqtt> {
|
|
17
|
+
constructor(log: AnsiLogger, config: Config, deviceConfig: DeviceConfigMqtt);
|
|
18
|
+
}
|
|
19
|
+
export declare class DysonMqttConnectionIoT extends DysonMqttConnection<DeviceConfigIoT> {
|
|
20
|
+
constructor(log: AnsiLogger, config: Config, deviceConfig: DeviceConfigIoT);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=dyson-mqtt-connect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dyson-mqtt-connect.d.ts","sourceRoot":"","sources":["../src/dyson-mqtt-connect.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAW,cAAc,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAC3D,OAAO,EACH,MAAM,EACN,YAAY,EACZ,eAAe,EACf,gBAAgB,EACnB,MAAM,mBAAmB,CAAC;AAW3B,8BAAsB,mBAAmB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY;IAcvE,QAAQ,CAAC,GAAG,EAAY,UAAU;IAClC,QAAQ,CAAC,MAAM,EAAS,MAAM;IAC9B,QAAQ,CAAC,YAAY,EAAG,CAAC;IAb7B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAG1B,SAAS,kBAA+B;IAGxC,OAAO,SAAuB;IAC9B,WAAW,CAAC,EAAI,MAAM,CAAC;gBAIV,GAAG,EAAY,UAAU,EACzB,MAAM,EAAS,MAAM,EACrB,YAAY,EAAG,CAAC,EACzB,SAAS,EAAe,MAAM,EAC9B,YAAY,EAAY,cAAc;IAgEpC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB1B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAM9B;AAGD,qBAAa,wBAAyB,SAAQ,mBAAmB,CAAC,gBAAgB,CAAC;gBAGnE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,gBAAgB;CAQ9E;AAGD,qBAAa,sBAAuB,SAAQ,mBAAmB,CAAC,eAAe,CAAC;gBAGhE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe;CAa7E"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Matterbridge plugin for Dyson robot vacuum and air treatment devices
|
|
2
|
+
// Copyright © 2025 Alexander Thoukydides
|
|
3
|
+
import { connect } from 'mqtt';
|
|
4
|
+
import { formatMilliseconds, logError, MS } from './utils.js';
|
|
5
|
+
import { setTimeout } from 'node:timers/promises';
|
|
6
|
+
// Reconnection back-off timings
|
|
7
|
+
const BACKOFF_MIN = 1 * MS; // 1 second minimum backoff
|
|
8
|
+
const BACKOFF_MAX = 60 * MS; // 1 minute maximum backoff
|
|
9
|
+
const BACKOFF_FACTOR = 2; // Double backoff on each failure
|
|
10
|
+
const BACKOFF_RESET_UPTIME = 10 * MS; // >10 second connection to use MIN
|
|
11
|
+
// Manage a Dyson MQTT broker connection
|
|
12
|
+
export class DysonMqttConnection {
|
|
13
|
+
log;
|
|
14
|
+
config;
|
|
15
|
+
deviceConfig;
|
|
16
|
+
// The MQTT client
|
|
17
|
+
mqtt;
|
|
18
|
+
// Abandon reconnection attempts
|
|
19
|
+
terminate = new AbortController();
|
|
20
|
+
// Backoff for reconnection attempts
|
|
21
|
+
backoff = BACKOFF_MIN;
|
|
22
|
+
uptimeStart;
|
|
23
|
+
// Construct a new MQTT client
|
|
24
|
+
constructor(log, config, deviceConfig, brokerUrl, extraOptions) {
|
|
25
|
+
this.log = log;
|
|
26
|
+
this.config = config;
|
|
27
|
+
this.deviceConfig = deviceConfig;
|
|
28
|
+
// MQTT debug logging, if enabled
|
|
29
|
+
let mqttLog;
|
|
30
|
+
if (this.config.debugFeatures.includes('Log MQTT Client')) {
|
|
31
|
+
mqttLog = (...args) => { this.log.debug('MQTT client:', ...args); };
|
|
32
|
+
}
|
|
33
|
+
// Common MQTT options
|
|
34
|
+
const options = {
|
|
35
|
+
log: mqttLog,
|
|
36
|
+
keepalive: 10, // Max 10 seconds between packets
|
|
37
|
+
manualConnect: true, // Disable automatic (re)connection
|
|
38
|
+
reconnectOnConnackError: false, // Disable automatic connection retry
|
|
39
|
+
reconnectPeriod: 0, // Disable automatic reconnection
|
|
40
|
+
resubscribe: true, // Resubscribe to topics on reconnect
|
|
41
|
+
rejectUnauthorized: false, // Allow self-signed certificates
|
|
42
|
+
protocolId: 'MQIsdp', // MQTT version 3.1
|
|
43
|
+
protocolVersion: 3,
|
|
44
|
+
...extraOptions
|
|
45
|
+
};
|
|
46
|
+
// Create and configure the MQTT client
|
|
47
|
+
this.mqtt = connect(brokerUrl, options);
|
|
48
|
+
this.mqtt.on('connect', () => {
|
|
49
|
+
// Successful connection, so start measuring uptime
|
|
50
|
+
log.info('MQTT client connected to broker');
|
|
51
|
+
if (this.uptimeStart !== undefined)
|
|
52
|
+
this.log.warn("Unexpected MQTT 'connect' event");
|
|
53
|
+
this.uptimeStart = Date.now();
|
|
54
|
+
}).on('error', err => {
|
|
55
|
+
// Connection attempt failed
|
|
56
|
+
log.error(`MQTT client connection error: ${err.message}`);
|
|
57
|
+
// (Will be followed by 'close' event to trigger reconnection)
|
|
58
|
+
}).on('close', () => {
|
|
59
|
+
// Connection closed, so adjust backoff and retry
|
|
60
|
+
if (this.terminate.signal.aborted) {
|
|
61
|
+
this.log.info('MQTT client connection closed; not reconnecting');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
else if (this.uptimeStart) {
|
|
65
|
+
const uptime = Date.now() - this.uptimeStart;
|
|
66
|
+
this.uptimeStart = undefined;
|
|
67
|
+
const description = `MQTT client closed stream after ${formatMilliseconds(uptime)}`;
|
|
68
|
+
if (uptime < BACKOFF_RESET_UPTIME) {
|
|
69
|
+
// Short connection, so keep the (increased) backoff
|
|
70
|
+
this.log.warn(description);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// Stable connection, so reset backoff to its minimum
|
|
74
|
+
this.log.info(`${description}; resetting reconnection backoff to minimum`);
|
|
75
|
+
this.backoff = BACKOFF_MIN;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
this.log.info('MQTT client closed stream after failed connection attempt');
|
|
80
|
+
}
|
|
81
|
+
// Try to (re)establish the connection again
|
|
82
|
+
void this.reconnect();
|
|
83
|
+
});
|
|
84
|
+
// Attempt the initial connection
|
|
85
|
+
this.log.info('Starting MQTT client...');
|
|
86
|
+
this.mqtt.connect();
|
|
87
|
+
}
|
|
88
|
+
// Attempt a reconnection
|
|
89
|
+
async reconnect() {
|
|
90
|
+
try {
|
|
91
|
+
// Wait for the backoff before reconnecting
|
|
92
|
+
this.log.info(`MQTT client reconnecting in ${formatMilliseconds(this.backoff)}...`);
|
|
93
|
+
const { signal } = this.terminate;
|
|
94
|
+
await setTimeout(this.backoff, undefined, { signal });
|
|
95
|
+
// Attempt the reconnection
|
|
96
|
+
this.log.info('MQTT client attempting reconnection...');
|
|
97
|
+
this.mqtt.reconnect();
|
|
98
|
+
// Increase backoff for the next attempt
|
|
99
|
+
this.backoff = Math.min(this.backoff * BACKOFF_FACTOR, BACKOFF_MAX);
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
if (!(err instanceof Error && err.name === 'AbortError')) {
|
|
103
|
+
logError(this.log, 'MQTT Reconnect', err);
|
|
104
|
+
}
|
|
105
|
+
// (Don't retry if terminated or otherwise failed synchronously)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Stop the MQTT client
|
|
109
|
+
async stop() {
|
|
110
|
+
this.log.info('Stopping MQTT client...');
|
|
111
|
+
this.terminate.abort();
|
|
112
|
+
await this.mqtt.endAsync();
|
|
113
|
+
this.log.info('MQTT client stopped');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Manage a local Dyson MQTT broker connection
|
|
117
|
+
export class DysonMqttConnectionLocal extends DysonMqttConnection {
|
|
118
|
+
// Construct a new MQTT client
|
|
119
|
+
constructor(log, config, deviceConfig) {
|
|
120
|
+
const brokerUrl = `mqtt://${deviceConfig.host}:${deviceConfig.port}`;
|
|
121
|
+
const options = {
|
|
122
|
+
username: deviceConfig.username,
|
|
123
|
+
password: deviceConfig.password
|
|
124
|
+
};
|
|
125
|
+
super(log, config, deviceConfig, brokerUrl, options);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Manage a Dyson IoT cloud MQTT broker connection
|
|
129
|
+
export class DysonMqttConnectionIoT extends DysonMqttConnection {
|
|
130
|
+
// Construct a new MQTT client
|
|
131
|
+
constructor(log, config, deviceConfig) {
|
|
132
|
+
const brokerUrl = `wss://${deviceConfig.endpoint}/mqtt`;
|
|
133
|
+
const headers = {
|
|
134
|
+
[deviceConfig.token_key]: deviceConfig.token_value,
|
|
135
|
+
'X-Amz-CustomAuthorizer-Name': deviceConfig.custom_authorizer_name,
|
|
136
|
+
'X-Amz-CustomAuthorizer-Signature': deviceConfig.token_signature
|
|
137
|
+
};
|
|
138
|
+
const options = {
|
|
139
|
+
clientId: deviceConfig.client_id,
|
|
140
|
+
wsOptions: { headers }
|
|
141
|
+
};
|
|
142
|
+
super(log, config, deviceConfig, brokerUrl, options);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=dyson-mqtt-connect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dyson-mqtt-connect.js","sourceRoot":"","sources":["../src/dyson-mqtt-connect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DysonMsg } from './dyson-types.js';
|
|
2
|
+
import { AnsiLogger } from 'matterbridge/logger';
|
|
3
|
+
export type DysonMqttFiltered = 'duplicate' | 'reordered';
|
|
4
|
+
export declare class DysonMQTTFilter {
|
|
5
|
+
readonly log: AnsiLogger;
|
|
6
|
+
readonly lastMsg: Map<string, DysonMsg>;
|
|
7
|
+
constructor(log: AnsiLogger);
|
|
8
|
+
filter(msg: DysonMsg): DysonMqttFiltered | undefined;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=dyson-mqtt-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dyson-mqtt-filter.d.ts","sourceRoot":"","sources":["../src/dyson-mqtt-filter.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjD,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,WAAW,CAAC;AAG1D,qBAAa,eAAe;IAMZ,QAAQ,CAAC,GAAG,EAAE,UAAU;IAHpC,QAAQ,CAAC,OAAO,wBAA6B;gBAGxB,GAAG,EAAE,UAAU;IAGpC,MAAM,CAAC,GAAG,EAAE,QAAQ,GAAG,iBAAiB,GAAG,SAAS;CAQvD"}
|