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,446 @@
1
+ // Matterbridge plugin for Dyson robot vacuum and air treatment devices
2
+ // Copyright © 2025 Alexander Thoukydides
3
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
4
+ var useValue = arguments.length > 2;
5
+ for (var i = 0; i < initializers.length; i++) {
6
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
7
+ }
8
+ return useValue ? value : void 0;
9
+ };
10
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
11
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
12
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
13
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
14
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
15
+ var _, done = false;
16
+ for (var i = decorators.length - 1; i >= 0; i--) {
17
+ var context = {};
18
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
19
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
20
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
21
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
22
+ if (kind === "accessor") {
23
+ if (result === void 0) continue;
24
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
25
+ if (_ = accept(result.get)) descriptor.get = _;
26
+ if (_ = accept(result.set)) descriptor.set = _;
27
+ if (_ = accept(result.init)) initializers.unshift(_);
28
+ }
29
+ else if (_ = accept(result)) {
30
+ if (kind === "field") initializers.unshift(_);
31
+ else descriptor[key] = _;
32
+ }
33
+ }
34
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
35
+ done = true;
36
+ };
37
+ import { DysonDevice } from './dyson-device-base.js';
38
+ import { DysonMqttAir } from './dyson-mqtt-air.js';
39
+ import { FanControl, ResourceMonitoring } from 'matterbridge/matter/clusters';
40
+ import { assertIsDefined, formatList, tryListener } from './utils.js';
41
+ import { EndpointsAir } from './endpoint-air.js';
42
+ import { PLUGIN_URL, VENDOR_NAME } from './settings.js';
43
+ import { mapDysonAirSensorStatus } from './dyson-device-air-quality.js';
44
+ import { DysonAirAnemometerControlProfile, DysonAirAnemometerControlTilt, DysonAirAutoMode, DysonAirFanAutoPower, DysonAirFanDirection, DysonAirFanPower, DysonAirFanSpeed, DysonAirFanState, DysonAirNightMode, DysonAirOscillation, DysonAirTiltAngle, DysonAirTiltOscillation } from './dyson-air-types.js';
45
+ import { CC, RI } from './logger-options.js';
46
+ import { ifValueChanged } from './decorator-changed.js';
47
+ // Mappings between FanMode and SpeedSetting
48
+ const FAN_MODE_TO_SPEED_LOW = 1;
49
+ const FAN_MODE_TO_SPEED_MEDIUM = 5;
50
+ const FAN_MODE_TO_SPEED_HIGH = 10;
51
+ const SPEED_TO_FAN_MODE_LOW = 3; // 1~3: Low
52
+ const SPEED_TO_FAN_MODE_MEDIUM = 6; // 4~6: Medium, 7~9: High
53
+ // Lifetime of a Pure (Hot+)Cool Link HEPA filter in operational hours
54
+ const PURE_LINK_FILTER_HOURS = 4300;
55
+ // Filter lifetime thresholds
56
+ const FILTER_CRITICAL = 0; // %
57
+ const FILTER_WARNING = 10; // %
58
+ // A Dyson air treatment device
59
+ let DysonDeviceAirBase = (() => {
60
+ let _classSuper = DysonDevice;
61
+ let _instanceExtraInitializers = [];
62
+ let _updateClusterAttributes_decorators;
63
+ return class DysonDeviceAirBase extends _classSuper {
64
+ static {
65
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
66
+ _updateClusterAttributes_decorators = [ifValueChanged];
67
+ __esDecorate(this, null, _updateClusterAttributes_decorators, { kind: "method", name: "updateClusterAttributes", static: false, private: false, access: { has: obj => "updateClusterAttributes" in obj, get: obj => obj.updateClusterAttributes }, metadata: _metadata }, null, _instanceExtraInitializers);
68
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
69
+ }
70
+ // The MQTT client and status update listener
71
+ static mqttConstructor = DysonMqttAir;
72
+ mqttListener = __runInitializers(this, _instanceExtraInitializers);
73
+ // The air purifier device endpoints
74
+ endpoints;
75
+ // Supported features
76
+ hasBreeze;
77
+ hasCarbonFilter;
78
+ hasDirection;
79
+ hasLeftRight;
80
+ hasUpDown;
81
+ // Should FanMode be used (in addition to On/Off) for fan off control
82
+ useFanModeOff = false;
83
+ // Construct a new Dyson device instance
84
+ constructor(...args) {
85
+ super(...args);
86
+ // Identify supported features from the presence of MQTT values
87
+ const { status } = this.mqtt;
88
+ this.hasBreeze = status.ancp !== undefined;
89
+ this.hasCarbonFilter = status.cflr !== undefined;
90
+ this.hasDirection = status.fdir !== undefined;
91
+ this.hasLeftRight = status.oson !== undefined;
92
+ this.hasUpDown = status.oton !== undefined;
93
+ // Prepare a listener for MQTT updates
94
+ this.mqttListener = tryListener(this.mqtt, () => this.updateClusterAttributes(this.mqtt.status));
95
+ }
96
+ // Create the endpoint for this device
97
+ makeEndpoints(validatedNames) {
98
+ // Static configuration of the air purifier clusters
99
+ const endpointOptions = {
100
+ uniqueStorageKey: this.uniqueId,
101
+ matterbridgeDeviceName: this.deviceName,
102
+ validatedNames,
103
+ deviceBasicInformation: {
104
+ nodeLabel: this.deviceName,
105
+ partNumber: this.modelNumber,
106
+ productLabel: this.modelNumber,
107
+ productName: this.modelName,
108
+ productUrl: PLUGIN_URL,
109
+ serialNumber: this.serialNumber,
110
+ softwareVersion: this.mqtt.status.version,
111
+ uniqueId: this.uniqueId,
112
+ vendorName: VENDOR_NAME
113
+ },
114
+ fanControl: {
115
+ rockSupport: {
116
+ rockLeftRight: this.hasLeftRight,
117
+ rockUpDown: this.hasUpDown,
118
+ rockRound: false
119
+ },
120
+ windSupport: {
121
+ sleepWind: true, // Night mode
122
+ naturalWind: this.hasBreeze
123
+ },
124
+ directionSupport: this.hasDirection
125
+ },
126
+ hepaFilter: {
127
+ filterPartNumbers: this.classStatic.filters.hepa
128
+ },
129
+ carbonFilter: this.hasCarbonFilter ? {
130
+ filterPartNumbers: this.classStatic.filters.carbon
131
+ } : undefined,
132
+ sensors: this.sensorSupport
133
+ };
134
+ // Create the endpoint
135
+ return new EndpointsAir(this.log, this.config, endpointOptions);
136
+ }
137
+ // Install handlers
138
+ async installHandlers(endpoints) {
139
+ await endpoints.setFanControlHandlers({
140
+ onOff: onOff => this.setPower(onOff),
141
+ airflowDirection: airflowDirection => this.setDirection(airflowDirection === FanControl.AirflowDirection.Forward),
142
+ fanMode: fanMode => {
143
+ switch (fanMode) {
144
+ case FanControl.FanMode.Auto: return this.setFanAuto();
145
+ case FanControl.FanMode.Off: return this.setFanSpeed(0);
146
+ case FanControl.FanMode.Low: return this.setFanSpeed(FAN_MODE_TO_SPEED_LOW);
147
+ case FanControl.FanMode.Medium: return this.setFanSpeed(FAN_MODE_TO_SPEED_MEDIUM);
148
+ default: return this.setFanSpeed(FAN_MODE_TO_SPEED_HIGH);
149
+ }
150
+ },
151
+ percentSetting: percentSetting => this.setFanSpeed(Math.ceil(percentSetting / 10)),
152
+ rockSetting: async (rockSetting) => {
153
+ if (this.hasLeftRight)
154
+ await this.setOscillateLeftRight(!!rockSetting.rockLeftRight);
155
+ if (this.hasUpDown)
156
+ await this.setOscillateUpDown(!!rockSetting.rockUpDown);
157
+ },
158
+ speedSetting: speedSetting => this.setFanSpeed(speedSetting),
159
+ windSetting: async (windSetting) => {
160
+ await this.setNightMode(!!windSetting.sleepWind);
161
+ if (this.hasBreeze)
162
+ await this.setOscillateBreeze(!!windSetting.naturalWind);
163
+ }
164
+ });
165
+ }
166
+ // Determine which optional sensors are supported
167
+ get sensorSupport() {
168
+ const sensors = mapDysonAirSensorStatus(this.log, this.mqtt.status);
169
+ return {
170
+ voc: sensors.voc !== undefined,
171
+ co2: sensors.co2 !== undefined,
172
+ nox: sensors.nox !== undefined,
173
+ hcho: sensors.hcho !== undefined,
174
+ pm25: sensors.pm25 !== undefined,
175
+ pm10: sensors.pm10 !== undefined
176
+ };
177
+ }
178
+ // List of endpoint function names and descriptions to validate
179
+ getEntities() {
180
+ return [{
181
+ name: 'Air Purifier',
182
+ description: 'Fan speed/oscillation control and filter monitoring'
183
+ }, {
184
+ name: 'Air Quality Sensor',
185
+ description: 'Environmental sensor measurements'
186
+ }, {
187
+ name: 'Composed Air Purifier',
188
+ description: 'Air purifier with integrated thermostat and sensors'
189
+ }, {
190
+ name: 'Humidity Sensor',
191
+ description: 'Relative humidity measurement'
192
+ }, {
193
+ name: 'Temperature Sensor',
194
+ description: 'Temperature measurement'
195
+ }];
196
+ }
197
+ // Retrieve the root device endpoints after validation
198
+ getEndpoints(validatedNames) {
199
+ this.endpoints ??= this.makeEndpoints(validatedNames);
200
+ return this.endpoints.bridgedNodeEndpoints;
201
+ }
202
+ // Start the device after the endpoints are active
203
+ async start() {
204
+ assertIsDefined(this.endpoints);
205
+ this.mqtt.on('status', this.mqttListener);
206
+ await this.installHandlers(this.endpoints);
207
+ await this.updateClusterAttributes(this.mqtt.status);
208
+ }
209
+ // Stop the device when Matterbridge is shutting down
210
+ async stop() {
211
+ this.mqtt.off('status', this.mqttListener);
212
+ await super.stop();
213
+ }
214
+ // Switch the fan on or off (without changing auto mode, if possible)
215
+ async setPower(powerOn) {
216
+ const { fpwr, fmod } = this.mqtt.status;
217
+ const isOn = fpwr ? fpwr !== DysonAirFanPower.Off // Non-Link
218
+ : fmod !== DysonAirFanAutoPower.Off; // Link models
219
+ if (isOn === powerOn) {
220
+ this.log.info(`Fan is already ${powerOn ? 'on' : 'off'}; no action required`);
221
+ }
222
+ else if (powerOn) {
223
+ this.log.info('Switching on');
224
+ await this.setState({}); // (sets an appropriate on state)
225
+ }
226
+ else {
227
+ this.log.info('Switching off');
228
+ await this.setState(fpwr ? { fpwr: DysonAirFanPower.Off } // Non-Link
229
+ : { fmod: DysonAirFanAutoPower.Off }); // Link models
230
+ }
231
+ }
232
+ // Switch the fan on in auto mode
233
+ async setFanAuto() {
234
+ this.log.info('Enabling auto mode');
235
+ const { auto } = this.mqtt.status;
236
+ await this.setState(auto ? { auto: DysonAirAutoMode.Auto } // Non-Link
237
+ : { fmod: DysonAirFanAutoPower.Auto }); // Link models
238
+ }
239
+ // Set the airflow direction (all except Link models)
240
+ async setDirection(forward) {
241
+ const direction = forward ? 'Forward' : 'Backward';
242
+ this.log.info(`${direction} airflow`);
243
+ await this.setState({ fdir: DysonAirFanDirection[direction] });
244
+ }
245
+ // Set the fan speed
246
+ async setFanSpeed(speed) {
247
+ // Quantize and range check the speed
248
+ const fnsp = Math.min(Math.round(speed), 10);
249
+ if (fnsp < 1) {
250
+ this.log.info('Fan speed set to 0; turning power off');
251
+ this.useFanModeOff = true;
252
+ return this.setPower(false);
253
+ }
254
+ // Set the speed, ensuring that auto mode is disabled
255
+ this.log.info(`Setting fan speed to ${fnsp}`);
256
+ const { auto } = this.mqtt.status;
257
+ await this.setState(auto ? { fnsp, auto: DysonAirAutoMode.Manual } // Non-Link
258
+ : { fnsp, fmod: DysonAirFanAutoPower.Manual }); // Link models
259
+ }
260
+ // Set night mode
261
+ async setNightMode(night) {
262
+ const nightMode = night ? 'Night' : 'Day';
263
+ this.log.info(`${nightMode} mode`);
264
+ await this.setState({ nmod: DysonAirNightMode[nightMode] });
265
+ }
266
+ // Set horizontal oscillation (all except Big+Quiet models)
267
+ async setOscillateLeftRight(oscillate) {
268
+ this.log.info(`${oscillate ? 'Enabling' : 'disabling'} left/right oscillation`);
269
+ // Most models use 'ON'/'OFF', but some use 'OION'/'OIOF' instead
270
+ const { oson } = this.mqtt.status;
271
+ const isOI = oson === DysonAirOscillation.FixedOI
272
+ || oson === DysonAirOscillation.OscillatingOI;
273
+ const OSON_KEYS = [['Fixed', 'FixedOI'], ['Oscillating', 'OscillatingOI']];
274
+ const key = OSON_KEYS[oscillate ? 1 : 0][isOI ? 1 : 0];
275
+ await this.setState({ oson: DysonAirOscillation[key] });
276
+ }
277
+ // Set vertical oscillation (Big+Quiet models only)
278
+ async setOscillateUpDown(oscillate) {
279
+ this.log.info(`${oscillate ? 'Enabling' : 'disabling'} up/down oscillation`);
280
+ const status = {};
281
+ if (oscillate) {
282
+ // Enable oscillation in breeze mode
283
+ status.oton = DysonAirTiltOscillation.Oscillating;
284
+ status.anct = DysonAirAnemometerControlTilt.Breeze;
285
+ status.otal = DysonAirTiltAngle.Breeze;
286
+ status.otau = DysonAirTiltAngle.Breeze;
287
+ }
288
+ else {
289
+ // Disable oscillation
290
+ status.oton = DysonAirTiltOscillation.Fixed;
291
+ status.anct = DysonAirAnemometerControlTilt.Custom;
292
+ const { otal, otau } = this.mqtt.status;
293
+ if (otal === DysonAirTiltAngle.Breeze || otau === DysonAirTiltAngle.Breeze) {
294
+ // Set an arbitrary tilt angle if breeze mode was enabled
295
+ status.otal = DysonAirTiltAngle.Degrees0;
296
+ status.otau = DysonAirTiltAngle.Degrees0;
297
+ }
298
+ }
299
+ await this.setState(status);
300
+ }
301
+ // Set breeze oscillation and fan speed (Humidify models only)
302
+ async setOscillateBreeze(breeze) {
303
+ this.log.info(`${breeze ? 'Enabling' : 'disabling'} breeze oscillation`);
304
+ const status = {
305
+ oson: DysonAirOscillation.Oscillating
306
+ };
307
+ if (breeze) {
308
+ // Enable oscillation in breeze mode
309
+ status.ancp = DysonAirAnemometerControlProfile.Breeze;
310
+ }
311
+ else {
312
+ // Enable non-breeze oscillation
313
+ const { ancp } = this.mqtt.status;
314
+ if (ancp === DysonAirAnemometerControlProfile.Breeze) {
315
+ // Set an arbitrary oscillation angle if breeze mode was enabled
316
+ status.ancp = DysonAirAnemometerControlProfile.Degrees180;
317
+ }
318
+ }
319
+ await this.setState(status);
320
+ }
321
+ // Send an MQTT command to set the product state
322
+ async setState(productState) {
323
+ // Also switch to an active power state, unless an alternative specified
324
+ const { fpwr, fmod } = this.mqtt.status;
325
+ if (fpwr === DysonAirFanPower.Off)
326
+ productState.fpwr ??= DysonAirFanPower.On; // Non-Link
327
+ if (fmod === DysonAirFanAutoPower.Off)
328
+ productState.fmod ??= DysonAirFanAutoPower.Manual; // Link models
329
+ // Publish the command
330
+ const values = Object.entries(productState).map(([key, value]) => `${CC}${key}=${value}${RI}`);
331
+ this.log.info(`Setting state: ${formatList(values)}`);
332
+ await this.mqtt.commandStateSet(productState);
333
+ // Ensure that the next MQTT status update is processed
334
+ this.changed.flush();
335
+ }
336
+ // Update cluster attributes when the MQTT status is updated
337
+ async updateClusterAttributes(status) {
338
+ const fanStatus = this.mapDysonFanControlStatus(status);
339
+ const filterStatus = this.mapDysonFilterStatus(status);
340
+ const sensorStatus = mapDysonAirSensorStatus(this.log, status);
341
+ await Promise.all([
342
+ this.endpoints?.updateFanControl(fanStatus),
343
+ this.endpoints?.updateFilterMonitoring(filterStatus),
344
+ this.endpoints?.updateSensors(sensorStatus)
345
+ ]);
346
+ }
347
+ // Convert the status to On/Off and Fan Control cluster attributes
348
+ mapDysonFanControlStatus(status) {
349
+ const { ancp, auto, fdir, fpwr, fmod, fnsp, fnst, nmod, oson, oton } = status;
350
+ // Start by determining the actual device state
351
+ const onOff = fpwr ? fpwr !== DysonAirFanPower.Off // Non-Link
352
+ : fmod !== DysonAirFanAutoPower.Off; // Link models
353
+ const isAuto = auto === DysonAirAutoMode.Auto // Non-Link
354
+ || fnsp === DysonAirFanSpeed.Auto; // Link
355
+ const isSpinning = fnst === DysonAirFanState.Running;
356
+ // Link models do not preserve speed setting in auto, so default to max
357
+ let speedSetting = typeof fnsp === 'number' ? fnsp : 10;
358
+ // Start by mapping the speed to a mode
359
+ let fanMode = FanControl.FanMode[speedSetting <= SPEED_TO_FAN_MODE_LOW ? 'Low'
360
+ : speedSetting <= SPEED_TO_FAN_MODE_MEDIUM ? 'Medium' : 'High'];
361
+ let speedCurrent;
362
+ if (!onOff) {
363
+ // Off, fan stopped
364
+ speedCurrent = 0;
365
+ if (this.useFanModeOff) {
366
+ // Fan Control was set to Off or 0 speed via Matter
367
+ fanMode = FanControl.FanMode.Off;
368
+ speedSetting = 0;
369
+ }
370
+ }
371
+ else {
372
+ this.useFanModeOff = false;
373
+ if (isAuto) {
374
+ // Auto mode: assume fan either at maximum speed or stopped
375
+ fanMode = FanControl.FanMode.Auto;
376
+ speedCurrent = isSpinning ? 10 : 0;
377
+ }
378
+ else {
379
+ // Manual mode: the fan is at the requested speed
380
+ speedCurrent = speedSetting;
381
+ }
382
+ }
383
+ // Night mode
384
+ const sleepWind = nmod === DysonAirNightMode.Night;
385
+ // Airflow direction
386
+ let airflowDirection;
387
+ if (this.hasDirection && fdir) {
388
+ // Non-Link models only
389
+ airflowDirection = FanControl.AirflowDirection[fdir === DysonAirFanDirection.Forward ? 'Forward' : 'Reverse'];
390
+ }
391
+ // Various oscillation modes
392
+ let rockLeftRight = false, rockUpDown = false, naturalWind = false;
393
+ if (this.hasLeftRight) {
394
+ // All except Big+Quiet models
395
+ rockLeftRight = oson === DysonAirOscillation.Oscillating
396
+ || oson === DysonAirOscillation.OscillatingOI;
397
+ }
398
+ if (this.hasUpDown) {
399
+ // Big+Quiet models only
400
+ rockUpDown = oton === DysonAirTiltOscillation.Oscillating;
401
+ }
402
+ if (this.hasBreeze) {
403
+ // Humidify+Cool models only
404
+ naturalWind = ancp === DysonAirAnemometerControlProfile.Breeze;
405
+ }
406
+ // Return the mapped values
407
+ let rockSetting;
408
+ if (this.hasLeftRight || this.hasUpDown)
409
+ rockSetting = { rockLeftRight, rockUpDown };
410
+ return {
411
+ airflowDirection,
412
+ fanMode,
413
+ onOff,
414
+ percentCurrent: speedCurrent * 10,
415
+ percentSetting: speedSetting * 10,
416
+ rockSetting,
417
+ speedCurrent,
418
+ speedSetting,
419
+ windSetting: { sleepWind, naturalWind }
420
+ };
421
+ }
422
+ // Convert the status to Filter Monitoring cluster attributes
423
+ mapDysonFilterStatus(status) {
424
+ const filterStatus = (percent) => {
425
+ const inPlaceIndicator = typeof percent === 'number';
426
+ const condition = inPlaceIndicator ? percent : 0;
427
+ const changeIndication = ResourceMonitoring.ChangeIndication[condition <= FILTER_CRITICAL ? 'Critical'
428
+ : condition <= FILTER_WARNING ? 'Warning' : 'Ok'];
429
+ return { condition, changeIndication, inPlaceIndicator };
430
+ };
431
+ // HEPA filter remaining life
432
+ let hepaPercent = status.hflr;
433
+ if (status.filf !== undefined) {
434
+ // For Pure (Hot+)Cool Link convert remaining hours to percentage
435
+ hepaPercent = Math.round(status.filf * 100 / PURE_LINK_FILTER_HOURS);
436
+ }
437
+ // Convert the filter status to Matter attribute representation
438
+ return {
439
+ hepa: filterStatus(hepaPercent),
440
+ carbon: this.hasCarbonFilter ? filterStatus(status.cflr) : undefined
441
+ };
442
+ }
443
+ };
444
+ })();
445
+ export { DysonDeviceAirBase };
446
+ //# sourceMappingURL=dyson-device-air-base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dyson-device-air-base.js","sourceRoot":"","sources":["../src/dyson-device-air-base.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,yCAAyC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEzC,OAAO,EACH,WAAW,EAEd,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACH,YAAY,EAGf,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACH,UAAU,EACV,kBAAkB,EACrB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EACH,YAAY,EAOf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EACH,gCAAgC,EAChC,6BAA6B,EAC7B,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,EAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,4CAA4C;AAC5C,MAAM,qBAAqB,GAAO,CAAC,CAAC;AACpC,MAAM,wBAAwB,GAAI,CAAC,CAAC;AACpC,MAAM,sBAAsB,GAAM,EAAE,CAAC;AACrC,MAAM,qBAAqB,GAAO,CAAC,CAAC,CAAC,WAAW;AAChD,MAAM,wBAAwB,GAAI,CAAC,CAAC,CAAC,yBAAyB;AAE9D,sEAAsE;AACtE,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAEpC,6BAA6B;AAC7B,MAAM,eAAe,GAAI,CAAC,CAAC,CAAC,IAAI;AAChC,MAAM,cAAc,GAAI,EAAE,CAAC,CAAC,IAAI;AAEhC,+BAA+B;IACT,kBAAkB;sBAAS,WAAW;;;iBAAtC,kBAAmB,SAAQ,WAAyB;;;mDAoSrE,cAAc;YACf,wNAAM,uBAAuB,6DAW5B;;;QA9SD,6CAA6C;QAC7C,MAAM,CAAU,eAAe,GAAG,YAAY,CAAC;QAC/C,YAAY,GAJM,mDAAkB,CAIL;QAE/B,oCAAoC;QACpC,SAAS,CAAwB;QAEjC,qBAAqB;QACrB,SAAS,CAAmB;QAC5B,eAAe,CAAa;QAC5B,YAAY,CAAgB;QAC5B,YAAY,CAAgB;QAC5B,SAAS,CAAmB;QAE5B,qEAAqE;QACrE,aAAa,GAAG,KAAK,CAAC;QAEtB,wCAAwC;QACxC,YAAY,GAAG,IAAgD;YAC3D,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;YAEf,+DAA+D;YAC/D,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAY,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC;YACpD,IAAI,CAAC,eAAe,GAAM,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC;YACpD,IAAI,CAAC,YAAY,GAAS,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC;YACpD,IAAI,CAAC,YAAY,GAAS,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC;YACpD,IAAI,CAAC,SAAS,GAAY,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC;YAEpD,sCAAsC;YACtC,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAC5C,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QACxD,CAAC;QAED,sCAAsC;QACtC,aAAa,CAAC,cAA4B;YACtC,oDAAoD;YACpD,MAAM,eAAe,GAAuB;gBACxC,gBAAgB,EAAQ,IAAI,CAAC,QAAQ;gBACrC,sBAAsB,EAAE,IAAI,CAAC,UAAU;gBACvC,cAAc;gBACd,sBAAsB,EAAE;oBACpB,SAAS,EAAW,IAAI,CAAC,UAAU;oBACnC,UAAU,EAAU,IAAI,CAAC,WAAW;oBACpC,YAAY,EAAQ,IAAI,CAAC,WAAW;oBACpC,WAAW,EAAS,IAAI,CAAC,SAAS;oBAClC,UAAU,EAAU,UAAU;oBAC9B,YAAY,EAAQ,IAAI,CAAC,YAAY;oBACrC,eAAe,EAAK,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO;oBAC5C,QAAQ,EAAY,IAAI,CAAC,QAAQ;oBACjC,UAAU,EAAU,WAAW;iBAClC;gBACD,UAAU,EAAE;oBACR,WAAW,EAAE;wBACT,aAAa,EAAG,IAAI,CAAC,YAAY;wBACjC,UAAU,EAAM,IAAI,CAAC,SAAS;wBAC9B,SAAS,EAAO,KAAK;qBACxB;oBACD,WAAW,EAAE;wBACT,SAAS,EAAO,IAAI,EAAE,aAAa;wBACnC,WAAW,EAAK,IAAI,CAAC,SAAS;qBACjC;oBACD,gBAAgB,EAAI,IAAI,CAAC,YAAY;iBACxC;gBACD,UAAU,EAAE;oBACR,iBAAiB,EAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI;iBACpD;gBACD,YAAY,EAAY,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;oBAC3C,iBAAiB,EAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM;iBACtD,CAAC,CAAC,CAAC,SAAS;gBACb,OAAO,EAAiB,IAAI,CAAC,aAAa;aAC7C,CAAC;YAEF,sBAAsB;YACtB,OAAO,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;QACpE,CAAC;QAED,mBAAmB;QACnB,KAAK,CAAC,eAAe,CAAC,SAAuB;YACzC,MAAM,SAAS,CAAC,qBAAqB,CAAC;gBAClC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBACpC,gBAAgB,EAAE,gBAAgB,CAAC,EAAE,CACjC,IAAI,CAAC,YAAY,CAAC,gBAAgB,KAAK,UAAU,CAAC,gBAAgB,CAAC,OAAO,CAAC;gBAC/E,OAAO,EAAE,OAAO,CAAC,EAAE;oBACf,QAAQ,OAAO,EAAE,CAAC;wBAClB,KAAK,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAG,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC;wBACzD,KAAK,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAI,OAAO,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;wBAC3D,KAAK,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAI,OAAO,IAAI,CAAC,WAAW,CAAC,qBAAqB,CAAC,CAAC;wBAC/E,KAAK,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,wBAAwB,CAAC,CAAC;wBAClF,OAAO,CAAC,CAAwB,OAAO,IAAI,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC;oBAChF,CAAC;gBACL,CAAC;gBACD,cAAc,EAAE,cAAc,CAAC,EAAE,CAC7B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC,CAAC;gBACpD,WAAW,EAAE,KAAK,EAAC,WAAW,EAAC,EAAE;oBAC7B,IAAI,IAAI,CAAC,YAAY;wBAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;oBACtF,IAAI,IAAI,CAAC,SAAS;wBAAM,MAAM,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;gBACpF,CAAC;gBACD,YAAY,EAAE,YAAY,CAAC,EAAE,CACzB,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC;gBAClC,WAAW,EAAE,KAAK,EAAC,WAAW,EAAC,EAAE;oBAC7B,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;oBACjD,IAAI,IAAI,CAAC,SAAS;wBAAM,MAAM,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;gBACrF,CAAC;aACJ,CAAC,CAAC;QACP,CAAC;QAED,iDAAiD;QACjD,IAAI,aAAa;YACb,MAAM,OAAO,GAAG,uBAAuB,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACpE,OAAO;gBACH,GAAG,EAAK,OAAO,CAAC,GAAG,KAAM,SAAS;gBAClC,GAAG,EAAK,OAAO,CAAC,GAAG,KAAM,SAAS;gBAClC,GAAG,EAAK,OAAO,CAAC,GAAG,KAAM,SAAS;gBAClC,IAAI,EAAI,OAAO,CAAC,IAAI,KAAK,SAAS;gBAClC,IAAI,EAAI,OAAO,CAAC,IAAI,KAAK,SAAS;gBAClC,IAAI,EAAI,OAAO,CAAC,IAAI,KAAK,SAAS;aACrC,CAAC;QACN,CAAC;QAED,+DAA+D;QACtD,WAAW;YAChB,OAAO,CAAC;oBACJ,IAAI,EAAY,cAAc;oBAC9B,WAAW,EAAK,qDAAqD;iBACxE,EAAE;oBACC,IAAI,EAAY,oBAAoB;oBACpC,WAAW,EAAK,mCAAmC;iBACtD,EAAE;oBACC,IAAI,EAAY,uBAAuB;oBACvC,WAAW,EAAK,qDAAqD;iBACxE,EAAE;oBACC,IAAI,EAAY,iBAAiB;oBACjC,WAAW,EAAK,+BAA+B;iBAClD,EAAE;oBACC,IAAI,EAAY,oBAAoB;oBACpC,WAAW,EAAK,yBAAyB;iBAC5C,CAAC,CAAC;QACP,CAAC;QAED,sDAAsD;QAC7C,YAAY,CAAC,cAA4B;YAC9C,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;YACtD,OAAO,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC;QAC/C,CAAC;QAED,kDAAkD;QACzC,KAAK,CAAC,KAAK;YAChB,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAChC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;YAC1C,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC3C,MAAM,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzD,CAAC;QAED,qDAAqD;QAC5C,KAAK,CAAC,IAAI;YACf,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;YAC3C,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;QACvB,CAAC;QAED,qEAAqE;QACrE,KAAK,CAAC,QAAQ,CAAC,OAAgB;YAC3B,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;YACxC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,GAAG,CAAO,WAAW;gBACjD,CAAC,CAAC,IAAI,KAAK,oBAAoB,CAAC,GAAG,CAAC,CAAE,cAAc;YACtE,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;gBACnB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,sBAAsB,CAAC,CAAC;YAClF,CAAC;iBAAM,IAAI,OAAO,EAAE,CAAC;gBACjB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;gBAC9B,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,iCAAiC;YAC9D,CAAC;iBAAM,CAAC;gBACJ,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;gBAC/B,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,GAAG,EAAE,CAAO,WAAW;oBAClD,CAAC,CAAC,EAAE,IAAI,EAAE,oBAAoB,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,cAAc;YAClF,CAAC;QACL,CAAC;QAED,iCAAiC;QACjC,KAAK,CAAC,UAAU;YACZ,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YACpC,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;YAClC,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,IAAI,EAAE,CAAO,WAAW;gBACnD,CAAC,CAAC,EAAE,IAAI,EAAE,oBAAoB,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,cAAc;QACnF,CAAC;QAED,qDAAqD;QACrD,KAAK,CAAC,YAAY,CAAC,OAAgB;YAC/B,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC;YACnD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,SAAS,UAAU,CAAC,CAAC;YACtC,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,oBAAoB,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,oBAAoB;QACpB,KAAK,CAAC,WAAW,CAAC,KAAa;YAC3B,qCAAqC;YACrC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;YAC7C,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;gBACX,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;gBACvD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;gBAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAChC,CAAC;YAED,qDAAqD;YACrD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAC;YAC9C,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;YAClC,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,gBAAgB,CAAC,MAAM,EAAE,CAAU,WAAW;gBAC9D,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC,CAAI,cAAc;QAC9F,CAAC;QAED,iBAAiB;QACjB,KAAK,CAAC,YAAY,CAAC,KAAc;YAC7B,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;YAC1C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,SAAS,OAAO,CAAC,CAAC;YACnC,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAChE,CAAC;QAED,2DAA2D;QAC3D,KAAK,CAAC,qBAAqB,CAAC,SAAkB;YAC1C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,yBAAyB,CAAC,CAAC;YAEhF,iEAAiE;YACjE,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;YAClC,MAAM,IAAI,GAAG,IAAI,KAAK,mBAAmB,CAAC,OAAO;mBACpC,IAAI,KAAK,mBAAmB,CAAC,aAAa,CAAC;YACxD,MAAM,SAAS,GAAG,CAAC,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC,aAAa,EAAE,eAAe,CAAC,CAAU,CAAC;YACpF,MAAM,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACvD,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5D,CAAC;QAED,mDAAmD;QACnD,KAAK,CAAC,kBAAkB,CAAC,SAAkB;YACvC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,sBAAsB,CAAC,CAAC;YAC7E,MAAM,MAAM,GAA0B,EAAE,CAAC;YACzC,IAAI,SAAS,EAAE,CAAC;gBACZ,oCAAoC;gBACpC,MAAM,CAAC,IAAI,GAAG,uBAAuB,CAAC,WAAW,CAAC;gBAClD,MAAM,CAAC,IAAI,GAAG,6BAA6B,CAAC,MAAM,CAAC;gBACnD,MAAM,CAAC,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC;gBACvC,MAAM,CAAC,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC;YAC3C,CAAC;iBAAM,CAAC;gBACJ,sBAAsB;gBACtB,MAAM,CAAC,IAAI,GAAG,uBAAuB,CAAC,KAAK,CAAC;gBAC5C,MAAM,CAAC,IAAI,GAAG,6BAA6B,CAAC,MAAM,CAAC;gBACnD,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;gBACxC,IAAI,IAAI,KAAK,iBAAiB,CAAC,MAAM,IAAI,IAAI,KAAK,iBAAiB,CAAC,MAAM,EAAE,CAAC;oBACzE,yDAAyD;oBACzD,MAAM,CAAC,IAAI,GAAG,iBAAiB,CAAC,QAAQ,CAAC;oBACzC,MAAM,CAAC,IAAI,GAAG,iBAAiB,CAAC,QAAQ,CAAC;gBAC7C,CAAC;YACL,CAAC;YACD,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;QAED,8DAA8D;QAC9D,KAAK,CAAC,kBAAkB,CAAC,MAAe;YACpC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,qBAAqB,CAAC,CAAC;YACzE,MAAM,MAAM,GAA0B;gBAClC,IAAI,EAAE,mBAAmB,CAAC,WAAW;aACxC,CAAC;YACF,IAAI,MAAM,EAAE,CAAC;gBACT,oCAAoC;gBACpC,MAAM,CAAC,IAAI,GAAG,gCAAgC,CAAC,MAAM,CAAC;YAC1D,CAAC;iBAAM,CAAC;gBACJ,gCAAgC;gBAChC,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;gBAClC,IAAI,IAAI,KAAK,gCAAgC,CAAC,MAAM,EAAE,CAAC;oBACnD,gEAAgE;oBAChE,MAAM,CAAC,IAAI,GAAG,gCAAgC,CAAC,UAAU,CAAC;gBAC9D,CAAC;YACL,CAAC;YACD,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAChC,CAAC;QAED,gDAAgD;QAChD,KAAK,CAAC,QAAQ,CAAC,YAAmC;YAC9C,wEAAwE;YACxE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;YACxC,IAAI,IAAI,KAAK,gBAAgB,CAAC,GAAG;gBAAO,YAAY,CAAC,IAAI,KAAK,gBAAgB,CAAC,EAAE,CAAC,CAAU,WAAW;YACvG,IAAI,IAAI,KAAK,oBAAoB,CAAC,GAAG;gBAAG,YAAY,CAAC,IAAI,KAAK,oBAAoB,CAAC,MAAM,CAAC,CAAE,cAAc;YAE1G,sBAAsB;YACtB,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC,CAAC;YAC/F,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACtD,MAAM,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;YAE9C,uDAAuD;YACvD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACzB,CAAC;QAED,4DAA4D;QAE5D,KAAK,CAAC,uBAAuB,CACzB,MAA2C;YAE3C,MAAM,SAAS,GAAG,IAAI,CAAC,wBAAwB,CAAC,MAAM,CAAC,CAAC;YACxD,MAAM,YAAY,GAAG,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;YACvD,MAAM,YAAY,GAAG,uBAAuB,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC/D,MAAM,OAAO,CAAC,GAAG,CAAC;gBACd,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,SAAS,CAAC;gBAC3C,IAAI,CAAC,SAAS,EAAE,sBAAsB,CAAC,YAAY,CAAC;gBACpD,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,YAAY,CAAC;aAC9C,CAAC,CAAC;QACP,CAAC;QAED,kEAAkE;QAClE,wBAAwB,CAAC,MAA2C;YAChE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC;YAE9E,+CAA+C;YAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,GAAG,CAAU,WAAW;gBACpD,CAAC,CAAC,IAAI,KAAK,oBAAoB,CAAC,GAAG,CAAC,CAAK,cAAc;YAC1E,MAAM,MAAM,GAAG,IAAI,KAAK,gBAAgB,CAAC,IAAI,CAAe,WAAW;mBACxD,IAAI,KAAK,gBAAgB,CAAC,IAAI,CAAC,CAAc,OAAO;YACnE,MAAM,UAAU,GAAG,IAAI,KAAK,gBAAgB,CAAC,OAAO,CAAC;YAErD,uEAAuE;YACvE,IAAI,YAAY,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAExD,uCAAuC;YACvC,IAAI,OAAO,GAAG,UAAU,CAAC,OAAO,CAC5B,YAAY,IAAI,qBAAqB,CAAI,CAAC,CAAC,KAAK;gBAClD,CAAC,CAAC,YAAY,IAAI,wBAAwB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAClE,IAAI,YAAoB,CAAC;YACzB,IAAI,CAAC,KAAK,EAAE,CAAC;gBACT,mBAAmB;gBACnB,YAAY,GAAG,CAAC,CAAC;gBACjB,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;oBACrB,mDAAmD;oBACnD,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC;oBACjC,YAAY,GAAG,CAAC,CAAC;gBACrB,CAAC;YACL,CAAC;iBAAM,CAAC;gBACJ,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;gBAC3B,IAAI,MAAM,EAAE,CAAC;oBACT,2DAA2D;oBAC3D,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC;oBAClC,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;gBACvC,CAAC;qBAAM,CAAC;oBACJ,iDAAiD;oBACjD,YAAY,GAAG,YAAY,CAAC;gBAChC,CAAC;YACL,CAAC;YAED,aAAa;YACb,MAAM,SAAS,GAAG,IAAI,KAAK,iBAAiB,CAAC,KAAK,CAAC;YAEnD,oBAAoB;YACpB,IAAI,gBAAyD,CAAC;YAC9D,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,EAAE,CAAC;gBAC5B,uBAAuB;gBACvB,gBAAgB,GAAG,UAAU,CAAC,gBAAgB,CAC1C,IAAI,KAAK,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YACvE,CAAC;YAED,4BAA4B;YAC5B,IAAI,aAAa,GAAG,KAAK,EAAE,UAAU,GAAG,KAAK,EAAE,WAAW,GAAG,KAAK,CAAC;YACnE,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACpB,8BAA8B;gBAC9B,aAAa,GAAG,IAAI,KAAK,mBAAmB,CAAC,WAAW;uBACxC,IAAI,KAAK,mBAAmB,CAAC,aAAa,CAAC;YAC/D,CAAC;YACD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjB,wBAAwB;gBACxB,UAAU,GAAG,IAAI,KAAK,uBAAuB,CAAC,WAAW,CAAC;YAC9D,CAAC;YACD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjB,4BAA4B;gBAC5B,WAAW,GAAG,IAAI,KAAK,gCAAgC,CAAC,MAAM,CAAC;YACnE,CAAC;YAED,2BAA2B;YAC3B,IAAI,WAA0C,CAAC;YAC/C,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,SAAS;gBAAE,WAAW,GAAG,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;YACrF,OAAO;gBACH,gBAAgB;gBAChB,OAAO;gBACP,KAAK;gBACL,cAAc,EAAE,YAAY,GAAG,EAAE;gBACjC,cAAc,EAAE,YAAY,GAAG,EAAE;gBACjC,WAAW;gBACX,YAAY;gBACZ,YAAY;gBACZ,WAAW,EAAK,EAAE,SAAS,EAAE,WAAW,EAAE;aAC7C,CAAC;QACN,CAAC;QAED,6DAA6D;QAC7D,oBAAoB,CAAC,MAA2C;YAC5D,MAAM,YAAY,GAAG,CAAC,OAAoC,EAAmC,EAAE;gBAC3F,MAAM,gBAAgB,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC;gBACrD,MAAM,SAAS,GAAG,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;gBACjD,MAAM,gBAAgB,GAAG,kBAAkB,CAAC,gBAAgB,CACxD,SAAS,IAAI,eAAe,CAAC,CAAC,CAAC,UAAU;oBACzC,CAAC,CAAC,SAAS,IAAI,cAAc,CAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBACvD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,CAAC;YAC7D,CAAC,CAAC;YAEF,6BAA6B;YAC7B,IAAI,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC;YAC9B,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC5B,iEAAiE;gBACjE,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,GAAG,GAAG,GAAG,sBAAsB,CAAC,CAAC;YACzE,CAAC;YAED,+DAA+D;YAC/D,OAAO;gBACH,IAAI,EAAI,YAAY,CAAC,WAAW,CAAC;gBACjC,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;aACvE,CAAC;QACN,CAAC;;;SA3ZiB,kBAAkB"}
@@ -0,0 +1,52 @@
1
+ import { EntityName } from './config-types.js';
2
+ import { DysonDeviceAirBase } from './dyson-device-air-base.js';
3
+ import { DysonMqttStatusAir } from './dyson-mqtt-air.js';
4
+ import { DysonMqttStatus } from './dyson-mqtt.js';
5
+ import { EndpointsAir } from './endpoint-air.js';
6
+ import { AbstractConstructor } from './utils.js';
7
+ export declare function DysonDeviceAirWithHeat<TBase extends AbstractConstructor<DysonDeviceAirBase>>(Base: TBase): (abstract new (...args: any[]) => {
8
+ installHandlers(endpoints: EndpointsAir): Promise<void>;
9
+ setTargetTemperature(celsius: number): Promise<void>;
10
+ setHeating(heat: boolean): Promise<void>;
11
+ getEntities(): {
12
+ name: EntityName;
13
+ description: string;
14
+ }[];
15
+ updateClusterAttributes(status: DysonMqttStatus<DysonMqttStatusAir>): Promise<void>;
16
+ mqttListener: () => void;
17
+ endpoints?: EndpointsAir;
18
+ hasBreeze: boolean;
19
+ hasCarbonFilter: boolean;
20
+ hasDirection: boolean;
21
+ hasLeftRight: boolean;
22
+ hasUpDown: boolean;
23
+ useFanModeOff: boolean;
24
+ makeEndpoints(validatedNames: EntityName[]): EndpointsAir;
25
+ readonly sensorSupport: import("./endpoint-air.js").EndpointOptionsAirSensors;
26
+ getEndpoints(validatedNames: EntityName[]): import("matterbridge").MatterbridgeEndpoint[];
27
+ start(): Promise<void>;
28
+ stop(): Promise<void>;
29
+ setPower(powerOn: boolean): Promise<void>;
30
+ setFanAuto(): Promise<void>;
31
+ setDirection(forward: boolean): Promise<void>;
32
+ setFanSpeed(speed: number): Promise<void>;
33
+ setNightMode(night: boolean): Promise<void>;
34
+ setOscillateLeftRight(oscillate: boolean): Promise<void>;
35
+ setOscillateUpDown(oscillate: boolean): Promise<void>;
36
+ setOscillateBreeze(breeze: boolean): Promise<void>;
37
+ setState(productState: import("./dyson-mqtt-air.js").DysonMqttProductState): Promise<void>;
38
+ mapDysonFanControlStatus(status: DysonMqttStatus<DysonMqttStatusAir>): import("./endpoint-air.js").UpdateAirFan;
39
+ mapDysonFilterStatus(status: DysonMqttStatus<DysonMqttStatusAir>): import("./endpoint-air.js").UpdateAirFilterMonitoring;
40
+ changed: import("./decorator-changed.js").Changed;
41
+ readonly log: import("matterbridge/logger").AnsiLogger;
42
+ readonly config: import("./config-types.js").Config;
43
+ readonly device: import("./config-types.js").DeviceConfig;
44
+ readonly mqtt: import("./dyson-mqtt-air.js").DysonMqttAir;
45
+ readonly uniqueId: string;
46
+ readonly classStatic: typeof import("./dyson-device-base.js").DysonDevice;
47
+ readonly modelName: string;
48
+ readonly modelNumber: string;
49
+ readonly deviceName: string;
50
+ readonly serialNumber: string;
51
+ }) & TBase;
52
+ //# sourceMappingURL=dyson-device-air-heat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dyson-device-air-heat.d.ts","sourceRoot":"","sources":["../src/dyson-device-air-heat.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAmB,MAAM,YAAY,CAAC;AASlE,wBAAgB,sBAAsB,CAAC,KAAK,SAAS,mBAAmB,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,KAAK,2BAI5E,GAAG,EAAE;+BAKgB,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;kCAmBlC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;qBAOnC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;mBAOtB;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,EAAE;oCASvD,eAAe,CAAC,kBAAkB,CAAC,GAC5C,OAAO,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAkBvB"}
@@ -0,0 +1,71 @@
1
+ // Matterbridge plugin for Dyson robot vacuum and air treatment devices
2
+ // Copyright © 2025 Alexander Thoukydides
3
+ import { Thermostat } from 'matterbridge';
4
+ import { assertIsDefined } from './utils.js';
5
+ import { DysonAirFanState, DysonAirHeatingMode, DysonAirHeatingStatus } from './dyson-air-types.js';
6
+ import { numeric } from './dyson-device-air-quality.js';
7
+ // Mixin to add heating to a Dyson air treatment device
8
+ export function DysonDeviceAirWithHeat(Base) {
9
+ class DysonDeviceAirWithHeat extends Base {
10
+ // Mixin constructor
11
+ constructor(...args) {
12
+ super(...args);
13
+ }
14
+ // Install handlers
15
+ async installHandlers(endpoints) {
16
+ await super.installHandlers(endpoints);
17
+ await endpoints.setThermostatHandlers({
18
+ occupiedHeatingSetpoint: setpoint => this.setTargetTemperature(setpoint / 100),
19
+ systemMode: mode => {
20
+ switch (mode) {
21
+ case Thermostat.SystemMode.Heat:
22
+ case Thermostat.SystemMode.EmergencyHeat:
23
+ return this.setHeating(true);
24
+ case Thermostat.SystemMode.Off:
25
+ default:
26
+ return this.setHeating(false);
27
+ }
28
+ }
29
+ });
30
+ }
31
+ // Set the heating target temperature
32
+ async setTargetTemperature(celsius) {
33
+ this.log.info(`Enabling heating with target temperature ${celsius} °C`);
34
+ const hmod = DysonAirHeatingMode.Heat;
35
+ await this.setState({ hmax: celsius, hmod });
36
+ }
37
+ // Enable or disable heating
38
+ async setHeating(heat) {
39
+ this.log.info(`${heat ? 'Enabling' : 'Disabling'} heating`);
40
+ // Turn power on when enabling heating, but not when disabling
41
+ await this.setState({ hmod: DysonAirHeatingMode[heat ? 'Heat' : 'Cool'] });
42
+ }
43
+ // List of endpoint function names and descriptions to validate
44
+ getEntities() {
45
+ return [...super.getEntities(), {
46
+ name: 'Thermostat',
47
+ description: 'Heating control'
48
+ }];
49
+ }
50
+ // Update cluster attributes when the MQTT status is updated
51
+ async updateClusterAttributes(status) {
52
+ await super.updateClusterAttributes(status);
53
+ const { fnst, hmax, hmod, hsta, tact } = status;
54
+ assertIsDefined(hmax);
55
+ assertIsDefined(tact);
56
+ const heatingMode = hmod === DysonAirHeatingMode.Heat;
57
+ const heat = hsta === DysonAirHeatingStatus.Heating;
58
+ const fan = fnst === DysonAirFanState.Running;
59
+ await this.endpoints?.updateThermostat({
60
+ localTemperature: numeric(tact, 100), // centi-°C
61
+ occupiedHeatingSetpoint: hmax * 100, // centi-°C
62
+ piHeatingDemand: heat ? 100 : 0, // %
63
+ systemMode: Thermostat.SystemMode[heatingMode ? 'Heat' : 'Off'],
64
+ thermostatRunningState: { heat, fan }
65
+ });
66
+ }
67
+ }
68
+ ;
69
+ return DysonDeviceAirWithHeat;
70
+ }
71
+ //# sourceMappingURL=dyson-device-air-heat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dyson-device-air-heat.js","sourceRoot":"","sources":["../src/dyson-device-air-heat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { DysonMqttStatusAirSensor } from './dyson-mqtt-air.js';
2
+ import { UpdateAirSensors } from './endpoint-air.js';
3
+ import { AnsiLogger } from 'matterbridge/logger';
4
+ export declare function numeric(value: number | string, factor?: number): number | null;
5
+ export declare function numeric(value?: number | string, factor?: number): number | null | undefined;
6
+ export declare function mapDysonAirSensorStatus(log: AnsiLogger, status: DysonMqttStatusAirSensor): UpdateAirSensors;
7
+ //# sourceMappingURL=dyson-device-air-quality.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dyson-device-air-quality.d.ts","sourceRoot":"","sources":["../src/dyson-device-air-quality.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AA4EjD,wBAAgB,OAAO,CAAC,KAAK,EAAG,MAAM,GAAG,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AACjF,wBAAgB,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;AAQ7F,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,wBAAwB,GAAG,gBAAgB,CAqB3G"}