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.
Files changed (261) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +14 -0
  3. package/README.md +365 -0
  4. package/dist/async-eventemitter.d.ts +9 -0
  5. package/dist/async-eventemitter.d.ts.map +1 -0
  6. package/dist/async-eventemitter.js +35 -0
  7. package/dist/async-eventemitter.js.map +1 -0
  8. package/dist/check-configuration.d.ts +5 -0
  9. package/dist/check-configuration.d.ts.map +1 -0
  10. package/dist/check-configuration.js +35 -0
  11. package/dist/check-configuration.js.map +1 -0
  12. package/dist/check-versions.d.ts +3 -0
  13. package/dist/check-versions.d.ts.map +1 -0
  14. package/dist/check-versions.js +31 -0
  15. package/dist/check-versions.js.map +1 -0
  16. package/dist/config-types.d.ts +48 -0
  17. package/dist/config-types.d.ts.map +1 -0
  18. package/dist/config-types.js +4 -0
  19. package/dist/config-types.js.map +1 -0
  20. package/dist/decorator-changed.d.ts +16 -0
  21. package/dist/decorator-changed.d.ts.map +1 -0
  22. package/dist/decorator-changed.js +62 -0
  23. package/dist/decorator-changed.js.map +1 -0
  24. package/dist/dyson-360-msg-types.d.ts +113 -0
  25. package/dist/dyson-360-msg-types.d.ts.map +1 -0
  26. package/dist/dyson-360-msg-types.js +4 -0
  27. package/dist/dyson-360-msg-types.js.map +1 -0
  28. package/dist/dyson-360-types.d.ts +76 -0
  29. package/dist/dyson-360-types.d.ts.map +1 -0
  30. package/dist/dyson-360-types.js +72 -0
  31. package/dist/dyson-360-types.js.map +1 -0
  32. package/dist/dyson-air-msg-types.d.ts +99 -0
  33. package/dist/dyson-air-msg-types.d.ts.map +1 -0
  34. package/dist/dyson-air-msg-types.js +4 -0
  35. package/dist/dyson-air-msg-types.js.map +1 -0
  36. package/dist/dyson-air-sensor-types.d.ts +59 -0
  37. package/dist/dyson-air-sensor-types.d.ts.map +1 -0
  38. package/dist/dyson-air-sensor-types.js +4 -0
  39. package/dist/dyson-air-sensor-types.js.map +1 -0
  40. package/dist/dyson-air-state-types.d.ts +96 -0
  41. package/dist/dyson-air-state-types.d.ts.map +1 -0
  42. package/dist/dyson-air-state-types.js +4 -0
  43. package/dist/dyson-air-state-types.js.map +1 -0
  44. package/dist/dyson-air-types.d.ts +325 -0
  45. package/dist/dyson-air-types.d.ts.map +1 -0
  46. package/dist/dyson-air-types.js +382 -0
  47. package/dist/dyson-air-types.js.map +1 -0
  48. package/dist/dyson-device-360-base.d.ts +35 -0
  49. package/dist/dyson-device-360-base.d.ts.map +1 -0
  50. package/dist/dyson-device-360-base.js +233 -0
  51. package/dist/dyson-device-360-base.js.map +1 -0
  52. package/dist/dyson-device-360-commands.d.ts +7 -0
  53. package/dist/dyson-device-360-commands.d.ts.map +1 -0
  54. package/dist/dyson-device-360-commands.js +129 -0
  55. package/dist/dyson-device-360-commands.js.map +1 -0
  56. package/dist/dyson-device-360-faults-table.d.ts +20 -0
  57. package/dist/dyson-device-360-faults-table.d.ts.map +1 -0
  58. package/dist/dyson-device-360-faults-table.js +161 -0
  59. package/dist/dyson-device-360-faults-table.js.map +1 -0
  60. package/dist/dyson-device-360-faults.d.ts +10 -0
  61. package/dist/dyson-device-360-faults.d.ts.map +1 -0
  62. package/dist/dyson-device-360-faults.js +123 -0
  63. package/dist/dyson-device-360-faults.js.map +1 -0
  64. package/dist/dyson-device-360.d.ts +38 -0
  65. package/dist/dyson-device-360.d.ts.map +1 -0
  66. package/dist/dyson-device-360.js +45 -0
  67. package/dist/dyson-device-360.js.map +1 -0
  68. package/dist/dyson-device-air-base.d.ts +41 -0
  69. package/dist/dyson-device-air-base.d.ts.map +1 -0
  70. package/dist/dyson-device-air-base.js +446 -0
  71. package/dist/dyson-device-air-base.js.map +1 -0
  72. package/dist/dyson-device-air-heat.d.ts +52 -0
  73. package/dist/dyson-device-air-heat.d.ts.map +1 -0
  74. package/dist/dyson-device-air-heat.js +71 -0
  75. package/dist/dyson-device-air-heat.js.map +1 -0
  76. package/dist/dyson-device-air-quality.d.ts +7 -0
  77. package/dist/dyson-device-air-quality.d.ts.map +1 -0
  78. package/dist/dyson-device-air-quality.js +101 -0
  79. package/dist/dyson-device-air-quality.js.map +1 -0
  80. package/dist/dyson-device-air.d.ts +216 -0
  81. package/dist/dyson-device-air.d.ts.map +1 -0
  82. package/dist/dyson-device-air.js +80 -0
  83. package/dist/dyson-device-air.js.map +1 -0
  84. package/dist/dyson-device-base.d.ts +49 -0
  85. package/dist/dyson-device-base.d.ts.map +1 -0
  86. package/dist/dyson-device-base.js +46 -0
  87. package/dist/dyson-device-base.js.map +1 -0
  88. package/dist/dyson-device.d.ts +5 -0
  89. package/dist/dyson-device.d.ts.map +1 -0
  90. package/dist/dyson-device.js +43 -0
  91. package/dist/dyson-device.js.map +1 -0
  92. package/dist/dyson-mqtt-360.d.ts +16 -0
  93. package/dist/dyson-mqtt-360.d.ts.map +1 -0
  94. package/dist/dyson-mqtt-360.js +83 -0
  95. package/dist/dyson-mqtt-360.js.map +1 -0
  96. package/dist/dyson-mqtt-air.d.ts +48 -0
  97. package/dist/dyson-mqtt-air.d.ts.map +1 -0
  98. package/dist/dyson-mqtt-air.js +216 -0
  99. package/dist/dyson-mqtt-air.js.map +1 -0
  100. package/dist/dyson-mqtt-config.d.ts +6 -0
  101. package/dist/dyson-mqtt-config.d.ts.map +1 -0
  102. package/dist/dyson-mqtt-config.js +47 -0
  103. package/dist/dyson-mqtt-config.js.map +1 -0
  104. package/dist/dyson-mqtt-connect.d.ts +22 -0
  105. package/dist/dyson-mqtt-connect.d.ts.map +1 -0
  106. package/dist/dyson-mqtt-connect.js +145 -0
  107. package/dist/dyson-mqtt-connect.js.map +1 -0
  108. package/dist/dyson-mqtt-filter.d.ts +10 -0
  109. package/dist/dyson-mqtt-filter.d.ts.map +1 -0
  110. package/dist/dyson-mqtt-filter.js +25 -0
  111. package/dist/dyson-mqtt-filter.js.map +1 -0
  112. package/dist/dyson-mqtt-parse.d.ts +16 -0
  113. package/dist/dyson-mqtt-parse.d.ts.map +1 -0
  114. package/dist/dyson-mqtt-parse.js +100 -0
  115. package/dist/dyson-mqtt-parse.js.map +1 -0
  116. package/dist/dyson-mqtt-subscribe.d.ts +28 -0
  117. package/dist/dyson-mqtt-subscribe.d.ts.map +1 -0
  118. package/dist/dyson-mqtt-subscribe.js +85 -0
  119. package/dist/dyson-mqtt-subscribe.js.map +1 -0
  120. package/dist/dyson-mqtt.d.ts +51 -0
  121. package/dist/dyson-mqtt.d.ts.map +1 -0
  122. package/dist/dyson-mqtt.js +138 -0
  123. package/dist/dyson-mqtt.js.map +1 -0
  124. package/dist/dyson-types.d.ts +18 -0
  125. package/dist/dyson-types.d.ts.map +1 -0
  126. package/dist/dyson-types.js +20 -0
  127. package/dist/dyson-types.js.map +1 -0
  128. package/dist/endpoint-360-behavior.d.ts +55 -0
  129. package/dist/endpoint-360-behavior.d.ts.map +1 -0
  130. package/dist/endpoint-360-behavior.js +156 -0
  131. package/dist/endpoint-360-behavior.js.map +1 -0
  132. package/dist/endpoint-360-rvc.d.ts +14 -0
  133. package/dist/endpoint-360-rvc.d.ts.map +1 -0
  134. package/dist/endpoint-360-rvc.js +149 -0
  135. package/dist/endpoint-360-rvc.js.map +1 -0
  136. package/dist/endpoint-360.d.ts +35 -0
  137. package/dist/endpoint-360.d.ts.map +1 -0
  138. package/dist/endpoint-360.js +197 -0
  139. package/dist/endpoint-360.js.map +1 -0
  140. package/dist/endpoint-air-purifier.d.ts +24 -0
  141. package/dist/endpoint-air-purifier.d.ts.map +1 -0
  142. package/dist/endpoint-air-purifier.js +97 -0
  143. package/dist/endpoint-air-purifier.js.map +1 -0
  144. package/dist/endpoint-air-quality.d.ts +11 -0
  145. package/dist/endpoint-air-quality.d.ts.map +1 -0
  146. package/dist/endpoint-air-quality.js +104 -0
  147. package/dist/endpoint-air-quality.js.map +1 -0
  148. package/dist/endpoint-air-thermostat.d.ts +358 -0
  149. package/dist/endpoint-air-thermostat.d.ts.map +1 -0
  150. package/dist/endpoint-air-thermostat.js +67 -0
  151. package/dist/endpoint-air-thermostat.js.map +1 -0
  152. package/dist/endpoint-air.d.ts +125 -0
  153. package/dist/endpoint-air.d.ts.map +1 -0
  154. package/dist/endpoint-air.js +459 -0
  155. package/dist/endpoint-air.js.map +1 -0
  156. package/dist/endpoint-base.d.ts +36 -0
  157. package/dist/endpoint-base.d.ts.map +1 -0
  158. package/dist/endpoint-base.js +137 -0
  159. package/dist/endpoint-base.js.map +1 -0
  160. package/dist/error-360.d.ts +35 -0
  161. package/dist/error-360.d.ts.map +1 -0
  162. package/dist/error-360.js +105 -0
  163. package/dist/error-360.js.map +1 -0
  164. package/dist/index.d.ts +5 -0
  165. package/dist/index.d.ts.map +1 -0
  166. package/dist/index.js +8 -0
  167. package/dist/index.js.map +1 -0
  168. package/dist/logger-filter.d.ts +23 -0
  169. package/dist/logger-filter.d.ts.map +1 -0
  170. package/dist/logger-filter.js +104 -0
  171. package/dist/logger-filter.js.map +1 -0
  172. package/dist/logger-options.d.ts +18 -0
  173. package/dist/logger-options.d.ts.map +1 -0
  174. package/dist/logger-options.js +41 -0
  175. package/dist/logger-options.js.map +1 -0
  176. package/dist/logger-prefix.d.ts +12 -0
  177. package/dist/logger-prefix.d.ts.map +1 -0
  178. package/dist/logger-prefix.js +37 -0
  179. package/dist/logger-prefix.js.map +1 -0
  180. package/dist/periodic.d.ts +31 -0
  181. package/dist/periodic.d.ts.map +1 -0
  182. package/dist/periodic.js +102 -0
  183. package/dist/periodic.js.map +1 -0
  184. package/dist/platform.d.ts +18 -0
  185. package/dist/platform.d.ts.map +1 -0
  186. package/dist/platform.js +138 -0
  187. package/dist/platform.js.map +1 -0
  188. package/dist/settings.d.ts +9 -0
  189. package/dist/settings.d.ts.map +1 -0
  190. package/dist/settings.js +28 -0
  191. package/dist/settings.js.map +1 -0
  192. package/dist/ti/config-types-ti.d.ts +16 -0
  193. package/dist/ti/config-types-ti.d.ts.map +1 -0
  194. package/dist/ti/config-types-ti.js +65 -0
  195. package/dist/ti/config-types-ti.js.map +1 -0
  196. package/dist/ti/config-types.d.ts +37 -0
  197. package/dist/ti/config-types.d.ts.map +1 -0
  198. package/dist/ti/config-types.js +11 -0
  199. package/dist/ti/config-types.js.map +1 -0
  200. package/dist/ti/dyson-360-msg-types-ti.d.ts +22 -0
  201. package/dist/ti/dyson-360-msg-types-ti.d.ts.map +1 -0
  202. package/dist/ti/dyson-360-msg-types-ti.js +134 -0
  203. package/dist/ti/dyson-360-msg-types-ti.js.map +1 -0
  204. package/dist/ti/dyson-360-msg-types.d.ts +55 -0
  205. package/dist/ti/dyson-360-msg-types.d.ts.map +1 -0
  206. package/dist/ti/dyson-360-msg-types.js +13 -0
  207. package/dist/ti/dyson-360-msg-types.js.map +1 -0
  208. package/dist/ti/dyson-360-types-ti.d.ts +17 -0
  209. package/dist/ti/dyson-360-types-ti.d.ts.map +1 -0
  210. package/dist/ti/dyson-360-types-ti.js +94 -0
  211. package/dist/ti/dyson-360-types-ti.js.map +1 -0
  212. package/dist/ti/dyson-360-types.d.ts +40 -0
  213. package/dist/ti/dyson-360-types.d.ts.map +1 -0
  214. package/dist/ti/dyson-360-types.js +11 -0
  215. package/dist/ti/dyson-360-types.js.map +1 -0
  216. package/dist/ti/dyson-air-msg-types-ti.d.ts +19 -0
  217. package/dist/ti/dyson-air-msg-types-ti.d.ts.map +1 -0
  218. package/dist/ti/dyson-air-msg-types-ti.js +115 -0
  219. package/dist/ti/dyson-air-msg-types-ti.js.map +1 -0
  220. package/dist/ti/dyson-air-msg-types.d.ts +46 -0
  221. package/dist/ti/dyson-air-msg-types.d.ts.map +1 -0
  222. package/dist/ti/dyson-air-msg-types.js +15 -0
  223. package/dist/ti/dyson-air-msg-types.js.map +1 -0
  224. package/dist/ti/dyson-air-sensor-types-ti.d.ts +9 -0
  225. package/dist/ti/dyson-air-sensor-types-ti.d.ts.map +1 -0
  226. package/dist/ti/dyson-air-sensor-types-ti.js +68 -0
  227. package/dist/ti/dyson-air-sensor-types-ti.js.map +1 -0
  228. package/dist/ti/dyson-air-sensor-types.d.ts +16 -0
  229. package/dist/ti/dyson-air-sensor-types.d.ts.map +1 -0
  230. package/dist/ti/dyson-air-sensor-types.js +12 -0
  231. package/dist/ti/dyson-air-sensor-types.js.map +1 -0
  232. package/dist/ti/dyson-air-state-types-ti.d.ts +9 -0
  233. package/dist/ti/dyson-air-state-types-ti.d.ts.map +1 -0
  234. package/dist/ti/dyson-air-state-types-ti.js +105 -0
  235. package/dist/ti/dyson-air-state-types-ti.js.map +1 -0
  236. package/dist/ti/dyson-air-state-types.d.ts +16 -0
  237. package/dist/ti/dyson-air-state-types.d.ts.map +1 -0
  238. package/dist/ti/dyson-air-state-types.js +12 -0
  239. package/dist/ti/dyson-air-state-types.js.map +1 -0
  240. package/dist/ti/dyson-air-types-ti.d.ts +59 -0
  241. package/dist/ti/dyson-air-types-ti.d.ts.map +1 -0
  242. package/dist/ti/dyson-air-types-ti.js +385 -0
  243. package/dist/ti/dyson-air-types-ti.js.map +1 -0
  244. package/dist/ti/dyson-air-types.d.ts +166 -0
  245. package/dist/ti/dyson-air-types.d.ts.map +1 -0
  246. package/dist/ti/dyson-air-types.js +11 -0
  247. package/dist/ti/dyson-air-types.js.map +1 -0
  248. package/dist/ti/dyson-types-ti.d.ts +10 -0
  249. package/dist/ti/dyson-types-ti.d.ts.map +1 -0
  250. package/dist/ti/dyson-types-ti.js +29 -0
  251. package/dist/ti/dyson-types-ti.js.map +1 -0
  252. package/dist/ti/dyson-types.d.ts +19 -0
  253. package/dist/ti/dyson-types.d.ts.map +1 -0
  254. package/dist/ti/dyson-types.js +11 -0
  255. package/dist/ti/dyson-types.js.map +1 -0
  256. package/dist/utils.d.ts +24 -0
  257. package/dist/utils.d.ts.map +1 -0
  258. package/dist/utils.js +150 -0
  259. package/dist/utils.js.map +1 -0
  260. package/matterbridge-dyson-robot.schema.json +359 -0
  261. package/package.json +93 -0
@@ -0,0 +1,25 @@
1
+ // Matterbridge plugin for Dyson robot vacuum and air treatment devices
2
+ // Copyright © 2025 Alexander Thoukydides
3
+ import { isDeepStrictEqual } from 'node:util';
4
+ // Filter received Dyson MQTT messages for reordering and duplication
5
+ export class DysonMQTTFilter {
6
+ log;
7
+ // The most recent message of each type (indexed by msg)
8
+ lastMsg = new Map;
9
+ // Create a new message filter
10
+ constructor(log) {
11
+ this.log = log;
12
+ }
13
+ // Filter a received message
14
+ filter(msg) {
15
+ const lastMsg = this.lastMsg.get(msg.msg);
16
+ if (lastMsg) {
17
+ if (msg.time < lastMsg.time)
18
+ return 'reordered';
19
+ if (isDeepStrictEqual(msg, lastMsg))
20
+ return 'duplicate';
21
+ }
22
+ this.lastMsg.set(msg.msg, structuredClone(msg));
23
+ }
24
+ }
25
+ //# sourceMappingURL=dyson-mqtt-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dyson-mqtt-filter.js","sourceRoot":"","sources":["../src/dyson-mqtt-filter.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,yCAAyC;AAEzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAO9C,qEAAqE;AACrE,MAAM,OAAO,eAAe;IAMH;IAJrB,wDAAwD;IAC/C,OAAO,GAAG,IAAI,GAAqB,CAAC;IAE7C,8BAA8B;IAC9B,YAAqB,GAAe;QAAf,QAAG,GAAH,GAAG,CAAY;IAAG,CAAC;IAExC,4BAA4B;IAC5B,MAAM,CAAC,GAAa;QAChB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,OAAO,EAAE,CAAC;YACV,IAAI,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI;gBAAa,OAAO,WAAW,CAAC;YAC3D,IAAI,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC;gBAAK,OAAO,WAAW,CAAC;QAC/D,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC;IACpD,CAAC;CACJ"}
@@ -0,0 +1,16 @@
1
+ import { AnsiLogger } from 'matterbridge/logger';
2
+ import { CheckerT } from 'ts-interface-checker';
3
+ import { DysonMsg } from './dyson-types.js';
4
+ export type DysonMsgAny<T> = {
5
+ [K in keyof T]: T[K] extends DysonMsg ? T[K] : never;
6
+ }[keyof T];
7
+ export type DysonMsgTypeName<T> = Extract<keyof T, string>;
8
+ export type DysonMsgCheckers<T> = {
9
+ [K in DysonMsgTypeName<T>]: CheckerT<T[K]>;
10
+ };
11
+ export interface DysonMqttParserConfig<T> {
12
+ prefix: string;
13
+ checkers: DysonMsgCheckers<T>;
14
+ }
15
+ export declare function dysonMqttParse<T>(log: AnsiLogger, config: DysonMqttParserConfig<T>, topic: string, normalise: boolean, payload: Buffer): DysonMsgAny<T>;
16
+ //# sourceMappingURL=dyson-mqtt-parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dyson-mqtt-parse.d.ts","sourceRoot":"","sources":["../src/dyson-mqtt-parse.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAY,MAAM,qBAAqB,CAAC;AAE3D,OAAO,EAAE,QAAQ,EAAgB,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAK5C,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;KACxB,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK;CACvD,CAAC,MAAM,CAAC,CAAC,CAAC;AACX,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;AAC3D,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC;AAGjF,MAAM,WAAW,qBAAqB,CAAC,CAAC;IACpC,MAAM,EAAM,MAAM,CAAC;IACnB,QAAQ,EAAI,gBAAgB,CAAC,CAAC,CAAC,CAAC;CACnC;AAGD,wBAAgB,cAAc,CAAC,CAAC,EAC5B,GAAG,EAAS,UAAU,EACtB,MAAM,EAAM,qBAAqB,CAAC,CAAC,CAAC,EACpC,KAAK,EAAO,MAAM,EAClB,SAAS,EAAG,OAAO,EACnB,OAAO,EAAK,MAAM,GACnB,WAAW,CAAC,CAAC,CAAC,CA8EhB"}
@@ -0,0 +1,100 @@
1
+ // Matterbridge plugin for Dyson robot vacuum and air treatment devices
2
+ // Copyright © 2025 Alexander Thoukydides
3
+ import { assertIsDefined, getValidationTree } from './utils.js';
4
+ import { checkers as dysonMsgCheckers } from './ti/dyson-types.js';
5
+ import { INSPECT_VERBOSE } from './logger-options.js';
6
+ import { inspect } from 'util';
7
+ // Parse and check a received MQTT message
8
+ export function dysonMqttParse(log, config, topic, normalise, payload) {
9
+ // Parse a raw message buffer as JSON and normalise property names
10
+ const parseAndNormalise = (text) => {
11
+ try {
12
+ const parsed = JSON.parse(text);
13
+ return normalise ? normaliseKeys(parsed) : parsed;
14
+ }
15
+ catch (err) {
16
+ logCheckerValidation("error" /* LogLevel.ERROR */, text);
17
+ const message = err instanceof Error ? err.message : String(err);
18
+ throw new Error(`Failed to parse Dyson MQTT message as JSON: ${message}`);
19
+ }
20
+ };
21
+ // Check that a received message is known and matches the expected type
22
+ const assertIsDysonMsg = (payload) => {
23
+ // Check that the message is of the general form expected
24
+ const baseChecker = dysonMsgCheckers.DysonMsg;
25
+ if (!baseChecker.test(payload)) {
26
+ baseChecker.setReportedPath('DysonMsg');
27
+ const validation = baseChecker.validate(payload);
28
+ assertIsDefined(validation);
29
+ logCheckerValidation("error" /* LogLevel.ERROR */, payload, validation);
30
+ throw new Error('Unexpected structure of Dyson MQTT message');
31
+ }
32
+ // Check whether the message type is known
33
+ const checker = getCheckerForMsg(payload);
34
+ // Check that the message is of the form expected for this type
35
+ const validation = checker.validate(payload);
36
+ if (validation) {
37
+ logCheckerValidation("error" /* LogLevel.ERROR */, payload, validation);
38
+ throw new Error('Unexpected structure of Dyson MQTT message');
39
+ }
40
+ const strictValidation = checker.strictValidate(payload);
41
+ if (strictValidation) {
42
+ logCheckerValidation("warn" /* LogLevel.WARN */, payload, strictValidation);
43
+ // (Continue processing messages that include unexpected properties)
44
+ }
45
+ };
46
+ // Map a message name to the corresponding type checker
47
+ const getCheckerForMsg = (payload) => {
48
+ const { prefix, checkers } = config;
49
+ // Construct the type name for this message
50
+ const msgPascalCase = kebabToPascalCase(payload.msg);
51
+ const typeName = `${prefix}${msgPascalCase}`;
52
+ const assertIsTypeName = (name) => {
53
+ if (name in config.checkers)
54
+ return;
55
+ logCheckerValidation("error" /* LogLevel.ERROR */, payload);
56
+ throw new Error(`Unrecognised Dyson MQTT message type: ${name}`);
57
+ };
58
+ assertIsTypeName(typeName);
59
+ // Return the type checker
60
+ const checker = checkers[typeName];
61
+ checker.setReportedPath(String(typeName));
62
+ return checker;
63
+ };
64
+ // Log checker validation errors
65
+ const logCheckerValidation = (level, payload, errors) => {
66
+ // Log the formatted message
67
+ log.log(level, `MQTT topic '${topic}':`);
68
+ if (errors) {
69
+ const validationLines = getValidationTree(errors);
70
+ validationLines.forEach(line => { log.log(level, line); });
71
+ }
72
+ const payloadLines = inspect(payload, INSPECT_VERBOSE).split('\n');
73
+ payloadLines.forEach(line => { log.info(` ${line}`); });
74
+ };
75
+ // Parse and check the message
76
+ const msg = parseAndNormalise(payload.toString());
77
+ assertIsDysonMsg(msg);
78
+ return msg;
79
+ }
80
+ // Convert a string from kebab-case (or FLAMING-KEBAB-CASE) to PascalCase
81
+ function kebabToPascalCase(str) {
82
+ return str.toLowerCase().split('-')
83
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
84
+ .join('');
85
+ }
86
+ // Convert a string from kebab-case or 'space case' to camelCase
87
+ function kebabToCamelCase(str) {
88
+ return str.replace(/[-\s]([a-z])/g, (_, letter) => letter.toUpperCase());
89
+ }
90
+ // Recursively convert property names from snake-case to camelCase
91
+ function normaliseKeys(obj) {
92
+ if (Array.isArray(obj)) {
93
+ return obj.map(item => normaliseKeys(item));
94
+ }
95
+ if (obj !== null && typeof obj === 'object') {
96
+ return Object.fromEntries(Object.entries(obj).map(([key, value]) => [kebabToCamelCase(key), normaliseKeys(value)]));
97
+ }
98
+ return obj;
99
+ }
100
+ //# sourceMappingURL=dyson-mqtt-parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dyson-mqtt-parse.js","sourceRoot":"","sources":["../src/dyson-mqtt-parse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import EventEmitter from 'events';
2
+ import { AnsiLogger } from 'matterbridge/logger';
3
+ import { MqttClient } from 'mqtt';
4
+ import { Config } from './config-types.js';
5
+ export interface DysonMqttSubscribeConfig {
6
+ command: string;
7
+ subscribe: string[];
8
+ other?: string[];
9
+ }
10
+ export interface DysonMqttSubscribeEventMap {
11
+ error: [unknown];
12
+ subscribed: [];
13
+ }
14
+ export type DysonMqttTopic = 'subscribed' | 'command' | 'expected' | 'unexpected';
15
+ export declare class DysonMqttSubscribe extends EventEmitter<DysonMqttSubscribeEventMap> {
16
+ readonly log: AnsiLogger;
17
+ readonly mqtt: MqttClient;
18
+ readonly config: Config;
19
+ readonly topics: DysonMqttSubscribeConfig;
20
+ readonly root_topic: string;
21
+ readonly username: string;
22
+ constructor(log: AnsiLogger, mqtt: MqttClient, config: Config, topics: DysonMqttSubscribeConfig, root_topic: string, username: string);
23
+ subscribe(): Promise<void>;
24
+ checkTopic(topic: string): DysonMqttTopic;
25
+ get commandTopic(): string;
26
+ replaceTopicPlaceholders(topic: string): string;
27
+ }
28
+ //# sourceMappingURL=dyson-mqtt-subscribe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dyson-mqtt-subscribe.d.ts","sourceRoot":"","sources":["../src/dyson-mqtt-subscribe.ts"],"names":[],"mappings":"AAGA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAI3C,MAAM,WAAW,wBAAwB;IACrC,OAAO,EAAK,MAAM,CAAC;IACnB,SAAS,EAAG,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAM,MAAM,EAAE,CAAC;CACxB;AAGD,MAAM,WAAW,0BAA0B;IACvC,KAAK,EAAO,CAAC,OAAO,CAAC,CAAC;IACtB,UAAU,EAAE,EAAE,CAAC;CAClB;AAGD,MAAM,MAAM,cAAc,GAAI,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,YAAY,CAAC;AAGnF,qBAAa,kBAAmB,SAAQ,YAAY,CAAC,0BAA0B,CAAC;IAIxE,QAAQ,CAAC,GAAG,EAAY,UAAU;IAClC,QAAQ,CAAC,IAAI,EAAW,UAAU;IAClC,QAAQ,CAAC,MAAM,EAAS,MAAM;IAC9B,QAAQ,CAAC,MAAM,EAAS,wBAAwB;IAChD,QAAQ,CAAC,UAAU,EAAK,MAAM;IAC9B,QAAQ,CAAC,QAAQ,EAAO,MAAM;gBALrB,GAAG,EAAY,UAAU,EACzB,IAAI,EAAW,UAAU,EACzB,MAAM,EAAS,MAAM,EACrB,MAAM,EAAS,wBAAwB,EACvC,UAAU,EAAK,MAAM,EACrB,QAAQ,EAAO,MAAM;IAY5B,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBhC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc;IAqBzC,IAAI,YAAY,IAAI,MAAM,CAEzB;IAGD,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAKlD"}
@@ -0,0 +1,85 @@
1
+ // Matterbridge plugin for Dyson robot vacuum and air treatment devices
2
+ // Copyright © 2025 Alexander Thoukydides
3
+ import EventEmitter from 'events';
4
+ import { plural, tryListener } from './utils.js';
5
+ // Manage Dyson MQTT topic subscriptions
6
+ export class DysonMqttSubscribe extends EventEmitter {
7
+ log;
8
+ mqtt;
9
+ config;
10
+ topics;
11
+ root_topic;
12
+ username;
13
+ // Construct a new subscription manager
14
+ constructor(log, mqtt, config, topics, root_topic, username) {
15
+ super({ captureRejections: true });
16
+ this.log = log;
17
+ this.mqtt = mqtt;
18
+ this.config = config;
19
+ this.topics = topics;
20
+ this.root_topic = root_topic;
21
+ this.username = username;
22
+ // (Re)subscribe to topics when the MQTT (re)connects
23
+ mqtt.on('connect', tryListener(this, async (_connack) => {
24
+ await this.subscribe();
25
+ this.emit('subscribed');
26
+ }));
27
+ }
28
+ // Subscribe to topics when the client (re)connects
29
+ async subscribe() {
30
+ // Select the required subscription topics
31
+ const topics = this.topics.subscribe.map(t => this.replaceTopicPlaceholders(t));
32
+ if (this.config.wildcardTopic)
33
+ topics.push('#');
34
+ // Attempt the subscription
35
+ const grant = await this.mqtt.subscribeAsync(topics, { qos: 1 });
36
+ // Check whether
37
+ const failures = topics.filter(topic => !grant.some(g => g.topic === topic));
38
+ if (!grant.length) {
39
+ throw new Error(`MQTT subscribe unsuccessful: all ${plural(topics.length, 'topic')} rejected`);
40
+ }
41
+ else if (failures.length) {
42
+ this.log.warn('MQTT subscribe partially successful: '
43
+ + `${failures.length} of ${plural(topics.length, 'topic')} rejected`);
44
+ failures.forEach(topic => { this.log.warn(` '${topic}'`); });
45
+ }
46
+ else {
47
+ this.log.info(`MQTT subscribe successful: all ${plural(grant.length, 'topic')} granted`);
48
+ }
49
+ }
50
+ // Warn if a received message's topic is unexpected
51
+ checkTopic(topic) {
52
+ // Check whether the topic is expected
53
+ const isTopics = (topics) => topics.some(t => this.replaceTopicPlaceholders(t) === topic);
54
+ if (isTopics([this.topics.command]))
55
+ return 'command';
56
+ if (isTopics(this.topics.subscribe))
57
+ return 'subscribed';
58
+ if (isTopics(this.topics.other ?? []))
59
+ return 'expected';
60
+ // Attempt to diagnose common problems
61
+ const [root_topic, username] = topic.split('/', 2);
62
+ if (root_topic !== this.root_topic) {
63
+ this.log.warn('MQTT topic root (product type) mismatch:'
64
+ + ` expected '${this.root_topic}', received '${root_topic}'`);
65
+ }
66
+ else if (username !== this.username) {
67
+ this.log.warn('MQTT topic username (product serial number) mismatch:'
68
+ + ` expected '${this.username}', received '${username}'`);
69
+ }
70
+ else {
71
+ this.log.warn(`Unexpected MQTT topic received: ${topic}`);
72
+ }
73
+ return 'unexpected';
74
+ }
75
+ get commandTopic() {
76
+ return this.replaceTopicPlaceholders(this.topics.command);
77
+ }
78
+ // Replace any placeholders in a topic
79
+ replaceTopicPlaceholders(topic) {
80
+ return topic
81
+ .replace('@', this.root_topic)
82
+ .replace('@', this.username);
83
+ }
84
+ }
85
+ //# sourceMappingURL=dyson-mqtt-subscribe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dyson-mqtt-subscribe.js","sourceRoot":"","sources":["../src/dyson-mqtt-subscribe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import { AnsiLogger } from 'matterbridge/logger';
2
+ import { Config, DeviceConfig } from './config-types.js';
3
+ import EventEmitter from 'events';
4
+ import { DysonMsg } from './dyson-types.js';
5
+ import { DysonMqttParserConfig, DysonMsgAny } from './dyson-mqtt-parse.js';
6
+ import { DysonMqttSubscribeConfig } from './dyson-mqtt-subscribe.js';
7
+ import { DysonMqttFiltered } from './dyson-mqtt-filter.js';
8
+ import { AsyncEventEmitter } from './async-eventemitter.js';
9
+ export interface DysonMqttConfig<T> {
10
+ topics: DysonMqttSubscribeConfig;
11
+ messages: DysonMqttParserConfig<T>;
12
+ }
13
+ export interface DysonMqttStatusBase {
14
+ reachable: boolean;
15
+ initialised: boolean;
16
+ }
17
+ export type DysonMqttStatus<T> = T & DysonMqttStatusBase;
18
+ export interface DysonMqttEventMapBase {
19
+ error: [unknown];
20
+ subscribed: [];
21
+ status: [];
22
+ }
23
+ export interface DysonMqttEventMap<T> extends DysonMqttEventMapBase {
24
+ message: [DysonMsgAny<T>];
25
+ }
26
+ export type PublishArgs<T, O extends string> = {
27
+ [K in keyof T]: T[K] extends DysonMsg ? (Omit<T[K], O | 'msg'> extends Record<string, never> ? [T[K]['msg']] : [T[K]['msg'], Omit<T[K], O | 'msg'>]) : never;
28
+ }[keyof T];
29
+ export interface DysonMqttLike extends EventEmitter {
30
+ waitUntilInitialised(): Promise<void>;
31
+ stop(): Promise<void>;
32
+ }
33
+ export declare abstract class DysonMqtt<T, S> extends AsyncEventEmitter<DysonMqttEventMap<T>> implements DysonMqttLike {
34
+ readonly log: AnsiLogger;
35
+ readonly config: Config;
36
+ readonly deviceConfig: DeviceConfig;
37
+ readonly mqttConfig: DysonMqttConfig<T>;
38
+ private mqtt;
39
+ private mqttConnection;
40
+ private mqttSubscribe;
41
+ private mqttFilter;
42
+ readonly status: DysonMqttStatus<S>;
43
+ constructor(log: AnsiLogger, config: Config, deviceConfig: DeviceConfig, mqttConfig: DysonMqttConfig<T>);
44
+ downTimerHandle?: NodeJS.Timeout;
45
+ updateReachable(reachable: boolean): void;
46
+ waitUntilInitialised(): Promise<void>;
47
+ stop(): Promise<void>;
48
+ publish(...[msg, params]: PublishArgs<T, 'time'>): Promise<void>;
49
+ logPayload(direction: 'publish' | 'receive', topic: string, payload: DysonMsg, filter?: DysonMqttFiltered): void;
50
+ }
51
+ //# sourceMappingURL=dyson-mqtt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dyson-mqtt.d.ts","sourceRoot":"","sources":["../src/dyson-mqtt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,138 @@
1
+ // Matterbridge plugin for Dyson robot vacuum and air treatment devices
2
+ // Copyright © 2025 Alexander Thoukydides
3
+ import { INSPECT_SINGLE_LINE, MM, MP, MR, RD, SR, ST } from './logger-options.js';
4
+ import { MS, tryListener } from './utils.js';
5
+ import { isConfigMqtt } from './dyson-mqtt-config.js';
6
+ import { dysonMqttParse } from './dyson-mqtt-parse.js';
7
+ import { DysonMqttSubscribe } from './dyson-mqtt-subscribe.js';
8
+ import { DysonMqttConnectionIoT, DysonMqttConnectionLocal } from './dyson-mqtt-connect.js';
9
+ import { inspect } from 'util';
10
+ import { DysonMQTTFilter } from './dyson-mqtt-filter.js';
11
+ import { AsyncEventEmitter } from './async-eventemitter.js';
12
+ ;
13
+ // Minimum disconnection time before indicating device not reachable
14
+ const MIN_DOWN_TIME = 5 * MS; // 5 seconds
15
+ // Dyson MQTT client for all device types
16
+ export class DysonMqtt extends AsyncEventEmitter {
17
+ log;
18
+ config;
19
+ deviceConfig;
20
+ mqttConfig;
21
+ // The MQTT client
22
+ mqtt;
23
+ mqttConnection;
24
+ mqttSubscribe;
25
+ mqttFilter;
26
+ // The current status
27
+ status = {
28
+ reachable: false,
29
+ initialised: false
30
+ };
31
+ // Construct a new MQTT client
32
+ constructor(log, config, deviceConfig, mqttConfig) {
33
+ super({ captureRejections: true });
34
+ this.log = log;
35
+ this.config = config;
36
+ this.deviceConfig = deviceConfig;
37
+ this.mqttConfig = mqttConfig;
38
+ // Configure the MQTT client
39
+ this.mqttConnection = isConfigMqtt(deviceConfig)
40
+ ? new DysonMqttConnectionLocal(log, config, deviceConfig)
41
+ : new DysonMqttConnectionIoT(log, config, deviceConfig);
42
+ this.mqtt = this.mqttConnection.mqtt;
43
+ this.mqtt.on('close', tryListener(this, () => { this.updateReachable(false); }));
44
+ // Manage MQTT topic subscriptions
45
+ const { root_topic, username } = deviceConfig;
46
+ this.mqttSubscribe = new DysonMqttSubscribe(log, this.mqtt, config, mqttConfig.topics, root_topic, username);
47
+ this.mqttSubscribe.on('error', err => this.emit('error', err));
48
+ this.mqttSubscribe.on('subscribed', () => {
49
+ this.emit('subscribed');
50
+ this.updateReachable(true);
51
+ });
52
+ // Handle received MQTT messages
53
+ this.mqttFilter = new DysonMQTTFilter(log);
54
+ this.mqtt.on('message', tryListener(this, (topic, payload) => {
55
+ // Check the received topic and message
56
+ const topicStatus = this.mqttSubscribe.checkTopic(topic);
57
+ const normalise = topicStatus !== 'command';
58
+ const msg = dysonMqttParse(log, mqttConfig.messages, topic, normalise, payload);
59
+ const filter = this.mqttFilter.filter(msg);
60
+ // Dispatch the validated message and indicate a status update
61
+ this.logPayload('receive', topic, msg, filter);
62
+ if (!filter && topicStatus === 'subscribed') {
63
+ this.emit('message', msg);
64
+ this.emit('status');
65
+ }
66
+ }));
67
+ }
68
+ // Update the device reachability, with hysteresis on unreachable indications
69
+ downTimerHandle;
70
+ updateReachable(reachable) {
71
+ if (reachable) {
72
+ // Cancel any pending down timer
73
+ clearTimeout(this.downTimerHandle);
74
+ this.downTimerHandle = undefined;
75
+ // Mark the device as reachable
76
+ if (!this.status.reachable) {
77
+ this.status.reachable = true;
78
+ this.emit('status');
79
+ }
80
+ }
81
+ else {
82
+ // Only start down timer if not already running
83
+ if (!this.downTimerHandle && this.status.reachable) {
84
+ this.downTimerHandle = setTimeout(() => {
85
+ // Mark the device as not reachable after a delay
86
+ this.log.error('MQTT client is down');
87
+ this.downTimerHandle = undefined;
88
+ this.status.reachable = false;
89
+ this.emit('status');
90
+ }, MIN_DOWN_TIME);
91
+ }
92
+ }
93
+ }
94
+ // Wait until the device is reachable and all initial state has been received
95
+ async waitUntilInitialised() {
96
+ while (!this.status.reachable || !this.status.initialised) {
97
+ await this.onceAsync('status');
98
+ }
99
+ }
100
+ // Stop the MQTT client
101
+ async stop() {
102
+ await this.mqttConnection.stop();
103
+ }
104
+ // Publish a command
105
+ async publish(...[msg, params]) {
106
+ // Construct the full message
107
+ const time = new Date().toISOString();
108
+ const fullMsg = { msg, ...params, time };
109
+ const payload = JSON.stringify(fullMsg);
110
+ // Publish to the command topic
111
+ const topic = this.mqttSubscribe.commandTopic;
112
+ this.logPayload('publish', topic, fullMsg);
113
+ await this.mqtt.publishAsync(topic, payload, { qos: 1, retain: false });
114
+ }
115
+ // Log received or transmitted message payloads
116
+ logPayload(direction, topic, payload, filter) {
117
+ if (!this.config.debugFeatures.includes('Log MQTT Payloads'))
118
+ return;
119
+ // List the fixed fields first
120
+ const { msg, time, ...other } = payload;
121
+ const properties = [
122
+ `msg: ${direction === 'publish' ? MP : MR}'${msg}'${MM}`,
123
+ `time: ${RD}'${time}'${MM}`
124
+ ];
125
+ // Include the other fields from the message, unless it is a duplicate
126
+ if (filter === 'duplicate') {
127
+ properties.push('...');
128
+ }
129
+ else {
130
+ properties.push(...Object.entries(other).sort().map(([key, value]) => `${key}: ${inspect(value, INSPECT_SINGLE_LINE)}`));
131
+ }
132
+ // Log the message (formatting with strikethrough if dropped by filter)
133
+ const object = `${MM}{ ${properties.join(', ')} }${RD}`;
134
+ this.log.debug(filter ? `MQTT ${direction}: ${ST}${object}${SR} (${filter})`
135
+ : `MQTT ${direction}: ${object} topic '${topic}'`);
136
+ }
137
+ }
138
+ //# sourceMappingURL=dyson-mqtt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dyson-mqtt.js","sourceRoot":"","sources":["../src/dyson-mqtt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface DysonMsg {
2
+ msg: string;
3
+ time: string;
4
+ }
5
+ export declare enum DysonModeReason {
6
+ Unknown = "",
7
+ LocalApp = "LAPP",
8
+ LocalSchedule = "LSCH",
9
+ RemoteApp = "RAPP",
10
+ Preconditioning = "PRC",
11
+ PhysicalUserInteraction = "PUI"
12
+ }
13
+ export declare enum DysonStateReason {
14
+ Environment = "ENV",
15
+ FLT = "FLT",
16
+ Mode = "MODE"
17
+ }
18
+ //# sourceMappingURL=dyson-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dyson-types.d.ts","sourceRoot":"","sources":["../src/dyson-types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,QAAQ;IACrB,GAAG,EAAqB,MAAM,CAAC;IAC/B,IAAI,EAAoB,MAAM,CAAC;CAClC;AAGD,oBAAY,eAAe;IACvB,OAAO,KAAqB;IAC5B,QAAQ,SAAwB;IAChC,aAAa,SAAmB;IAChC,SAAS,SAAuB;IAChC,eAAe,QAAgB;IAC/B,uBAAuB,QAAQ;CAClC;AAGD,oBAAY,gBAAgB;IACxB,WAAW,QAAoB;IAC/B,GAAG,QAA4B;IAC/B,IAAI,SAA4B;CACnC"}
@@ -0,0 +1,20 @@
1
+ // Matterbridge plugin for Dyson robot vacuum and air treatment devices
2
+ // Copyright © 2025 Alexander Thoukydides
3
+ // Dyson mode reason
4
+ export var DysonModeReason;
5
+ (function (DysonModeReason) {
6
+ DysonModeReason["Unknown"] = "";
7
+ DysonModeReason["LocalApp"] = "LAPP";
8
+ DysonModeReason["LocalSchedule"] = "LSCH";
9
+ DysonModeReason["RemoteApp"] = "RAPP";
10
+ DysonModeReason["Preconditioning"] = "PRC";
11
+ DysonModeReason["PhysicalUserInteraction"] = "PUI";
12
+ })(DysonModeReason || (DysonModeReason = {}));
13
+ // Dyson state reason
14
+ export var DysonStateReason;
15
+ (function (DysonStateReason) {
16
+ DysonStateReason["Environment"] = "ENV";
17
+ DysonStateReason["FLT"] = "FLT";
18
+ DysonStateReason["Mode"] = "MODE";
19
+ })(DysonStateReason || (DysonStateReason = {}));
20
+ //# sourceMappingURL=dyson-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dyson-types.js","sourceRoot":"","sources":["../src/dyson-types.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,yCAAyC;AAQzC,oBAAoB;AACpB,MAAM,CAAN,IAAY,eAOX;AAPD,WAAY,eAAe;IACvB,+BAA4B,CAAA;IAC5B,oCAAgC,CAAA;IAChC,yCAAgC,CAAA;IAChC,qCAAgC,CAAA;IAChC,0CAA+B,CAAA;IAC/B,kDAA+B,CAAA;AACnC,CAAC,EAPW,eAAe,KAAf,eAAe,QAO1B;AAED,qBAAqB;AACrB,MAAM,CAAN,IAAY,gBAIX;AAJD,WAAY,gBAAgB;IACxB,uCAA+B,CAAA;IAC/B,+BAA+B,CAAA;IAC/B,iCAAgC,CAAA;AACpC,CAAC,EAJW,gBAAgB,KAAhB,gBAAgB,QAI3B"}
@@ -0,0 +1,55 @@
1
+ import { Behavior, MaybePromise } from 'matterbridge/matter';
2
+ import { ModeBase, RvcOperationalState } from 'matterbridge/matter/clusters';
3
+ import { RvcCleanModeBehavior, RvcOperationalStateBehavior, RvcRunModeBehavior } from 'matterbridge/matter/behaviors';
4
+ import { AnsiLogger } from 'matterbridge/logger';
5
+ export declare enum RvcRunMode360 {
6
+ Idle = 0,
7
+ Cleaning = 1,
8
+ Mapping = 2
9
+ }
10
+ export declare enum RvcCleanMode360 {
11
+ Quiet = 0,// Eye: Quiet Heurist: Quiet Vis Nav: Quiet
12
+ Quick = 1,// Vis Nav: Quick
13
+ High = 2,// Heurist: High
14
+ MaxBoost = 3,// Eye: Max Heurist: Max Vis Nav: Boost
15
+ Auto = 4
16
+ }
17
+ export declare const VENDOR_ERROR_360 = 128;
18
+ export interface EndpointCommands360 {
19
+ ChangeRunMode: (newMode: RvcRunMode360) => MaybePromise;
20
+ ChangeCleanMode: (newMode: RvcCleanMode360) => MaybePromise;
21
+ Pause: () => MaybePromise;
22
+ Resume: () => MaybePromise;
23
+ GoHome: () => MaybePromise;
24
+ }
25
+ type EndpointCommand360Args<T extends keyof EndpointCommands360> = Parameters<EndpointCommands360[T]>;
26
+ export declare class BehaviorDevice360 {
27
+ readonly log: AnsiLogger;
28
+ readonly commands: Partial<EndpointCommands360>;
29
+ constructor(log: AnsiLogger);
30
+ setCommandHandler<Command extends keyof EndpointCommands360>(command: Command, handler: EndpointCommands360[Command]): void;
31
+ executeCommand<Command extends keyof EndpointCommands360>(command: Command, ...args: EndpointCommand360Args<Command>): Promise<void>;
32
+ }
33
+ export declare class Behavior360 extends Behavior {
34
+ static readonly id = "dyson-rvc";
35
+ state: Behavior360.State;
36
+ }
37
+ export declare namespace Behavior360 {
38
+ class State {
39
+ device: BehaviorDevice360;
40
+ }
41
+ }
42
+ export declare class RvcRunModeServer360 extends RvcRunModeBehavior {
43
+ changeToMode({ newMode }: ModeBase.ChangeToModeRequest): Promise<ModeBase.ChangeToModeResponse>;
44
+ }
45
+ export declare class RvcCleanModeServer360 extends RvcCleanModeBehavior {
46
+ changeToMode({ newMode }: ModeBase.ChangeToModeRequest): Promise<ModeBase.ChangeToModeResponse>;
47
+ }
48
+ export declare class RvcOperationalStateServer360 extends RvcOperationalStateBehavior {
49
+ command(command: 'Pause' | 'Resume' | 'GoHome', defaultErrorId: RvcOperationalState.ErrorState): Promise<RvcOperationalState.OperationalCommandResponse>;
50
+ pause(): Promise<RvcOperationalState.OperationalCommandResponse>;
51
+ resume(): Promise<RvcOperationalState.OperationalCommandResponse>;
52
+ goHome(): Promise<RvcOperationalState.OperationalCommandResponse>;
53
+ }
54
+ export {};
55
+ //# sourceMappingURL=endpoint-360-behavior.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpoint-360-behavior.d.ts","sourceRoot":"","sources":["../src/endpoint-360-behavior.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EACH,oBAAoB,EACpB,2BAA2B,EAC3B,kBAAkB,EACrB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAKjD,oBAAY,aAAa;IACrB,IAAI,IAAA;IACJ,QAAQ,IAAA;IACR,OAAO,IAAA;CACV;AAGD,oBAAY,eAAe;IACvB,KAAK,IAAA,CAAO,6CAA6C;IACzD,KAAK,IAAA,CAAO,6CAA6C;IACzD,IAAI,IAAA,CAAQ,4BAA4B;IACxC,QAAQ,IAAA,CAAI,6CAA6C;IACzD,IAAI,IAAA;CACP;AAGD,eAAO,MAAM,gBAAgB,MAAO,CAAC;AAGrC,MAAM,WAAW,mBAAmB;IAChC,aAAa,EAAI,CAAC,OAAO,EAAE,aAAa,KAAO,YAAY,CAAC;IAC5D,eAAe,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,YAAY,CAAC;IAC5D,KAAK,EAAY,MAA8B,YAAY,CAAC;IAC5D,MAAM,EAAW,MAA8B,YAAY,CAAC;IAC5D,MAAM,EAAW,MAA8B,YAAY,CAAC;CAC/D;AACD,KAAK,sBAAsB,CAAC,CAAC,SAAS,MAAM,mBAAmB,IAAI,UAAU,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;AAItG,qBAAa,iBAAiB;IAMd,QAAQ,CAAC,GAAG,EAAE,UAAU;IAHpC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAM;gBAGhC,GAAG,EAAE,UAAU;IAGpC,iBAAiB,CAAC,OAAO,SAAS,MAAM,mBAAmB,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,CAAC,OAAO,CAAC,GAAG,IAAI;IAMrH,cAAc,CAAC,OAAO,SAAS,MAAM,mBAAmB,EAC1D,OAAO,EAAK,OAAO,EACnB,GAAG,IAAI,EAAK,sBAAsB,CAAC,OAAO,CAAC,GAC5C,OAAO,CAAC,IAAI,CAAC;CAKnB;AACD,qBAAa,WAAY,SAAQ,QAAQ;IACrC,gBAAyB,EAAE,eAAe;IAClC,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;CACpC;AAED,yBAAiB,WAAW,CAAC;IACzB,MAAa,KAAK;QACd,MAAM,EAAG,iBAAiB,CAAC;KAC9B;CACJ;AAGD,qBAAa,mBAAoB,SAAQ,kBAAkB;IAGjD,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,QAAQ,CAAC,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC;CAoBxG;AAGD,qBAAa,qBAAsB,SAAQ,oBAAoB;IAGrD,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,QAAQ,CAAC,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAC;CAoBxG;AAGD,qBAAa,4BAA6B,SAAQ,2BAA2B;IAuBnE,OAAO,CACT,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,EACtC,cAAc,EAAE,mBAAmB,CAAC,UAAU,GAC/C,OAAO,CAAC,mBAAmB,CAAC,0BAA0B,CAAC;IAejD,KAAK,IAAK,OAAO,CAAC,mBAAmB,CAAC,0BAA0B,CAAC;IAKjE,MAAM,IAAI,OAAO,CAAC,mBAAmB,CAAC,0BAA0B,CAAC;IAKjE,MAAM,IAAI,OAAO,CAAC,mBAAmB,CAAC,0BAA0B,CAAC;CAG7E"}