@switchbot/homebridge-switchbot 5.0.0-beta.7 → 5.0.0-beta.70

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 (271) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +45 -3
  3. package/config.schema.json +871 -13754
  4. package/dist/devices-hap/airpurifier.d.ts.map +1 -1
  5. package/dist/devices-hap/airpurifier.js +12 -6
  6. package/dist/devices-hap/airpurifier.js.map +1 -1
  7. package/dist/devices-hap/blindtilt.js +3 -3
  8. package/dist/devices-hap/bot.d.ts.map +1 -1
  9. package/dist/devices-hap/bot.js +16 -5
  10. package/dist/devices-hap/bot.js.map +1 -1
  11. package/dist/devices-hap/ceilinglight.d.ts.map +1 -1
  12. package/dist/devices-hap/ceilinglight.js +13 -7
  13. package/dist/devices-hap/ceilinglight.js.map +1 -1
  14. package/dist/devices-hap/colorbulb.d.ts.map +1 -1
  15. package/dist/devices-hap/colorbulb.js +49 -9
  16. package/dist/devices-hap/colorbulb.js.map +1 -1
  17. package/dist/devices-hap/contact.js +3 -3
  18. package/dist/devices-hap/curtain.js +2 -2
  19. package/dist/devices-hap/curtain.js.map +1 -1
  20. package/dist/devices-hap/device.d.ts +18 -8
  21. package/dist/devices-hap/device.d.ts.map +1 -1
  22. package/dist/devices-hap/device.js +141 -69
  23. package/dist/devices-hap/device.js.map +1 -1
  24. package/dist/devices-hap/fan.d.ts.map +1 -1
  25. package/dist/devices-hap/fan.js +12 -6
  26. package/dist/devices-hap/fan.js.map +1 -1
  27. package/dist/devices-hap/hub.d.ts.map +1 -1
  28. package/dist/devices-hap/hub.js +6 -5
  29. package/dist/devices-hap/hub.js.map +1 -1
  30. package/dist/devices-hap/humidifier.d.ts +5 -0
  31. package/dist/devices-hap/humidifier.d.ts.map +1 -1
  32. package/dist/devices-hap/humidifier.js +92 -4
  33. package/dist/devices-hap/humidifier.js.map +1 -1
  34. package/dist/devices-hap/iosensor.d.ts.map +1 -1
  35. package/dist/devices-hap/iosensor.js +36 -21
  36. package/dist/devices-hap/iosensor.js.map +1 -1
  37. package/dist/devices-hap/lightstrip.d.ts.map +1 -1
  38. package/dist/devices-hap/lightstrip.js +38 -8
  39. package/dist/devices-hap/lightstrip.js.map +1 -1
  40. package/dist/devices-hap/lock.d.ts.map +1 -1
  41. package/dist/devices-hap/lock.js +14 -6
  42. package/dist/devices-hap/lock.js.map +1 -1
  43. package/dist/devices-hap/meter.d.ts.map +1 -1
  44. package/dist/devices-hap/meter.js +6 -5
  45. package/dist/devices-hap/meter.js.map +1 -1
  46. package/dist/devices-hap/meterplus.d.ts.map +1 -1
  47. package/dist/devices-hap/meterplus.js +6 -5
  48. package/dist/devices-hap/meterplus.js.map +1 -1
  49. package/dist/devices-hap/meterpro.d.ts.map +1 -1
  50. package/dist/devices-hap/meterpro.js +7 -6
  51. package/dist/devices-hap/meterpro.js.map +1 -1
  52. package/dist/devices-hap/motion.js +3 -3
  53. package/dist/devices-hap/plug.d.ts.map +1 -1
  54. package/dist/devices-hap/plug.js +11 -6
  55. package/dist/devices-hap/plug.js.map +1 -1
  56. package/dist/devices-hap/relayswitch.js +3 -3
  57. package/dist/devices-hap/robotvacuumcleaner.d.ts.map +1 -1
  58. package/dist/devices-hap/robotvacuumcleaner.js +13 -6
  59. package/dist/devices-hap/robotvacuumcleaner.js.map +1 -1
  60. package/dist/devices-hap/waterdetector.js +3 -3
  61. package/dist/devices-matter/BaseMatterAccessory.d.ts +27 -0
  62. package/dist/devices-matter/BaseMatterAccessory.d.ts.map +1 -1
  63. package/dist/devices-matter/BaseMatterAccessory.js +169 -5
  64. package/dist/devices-matter/BaseMatterAccessory.js.map +1 -1
  65. package/dist/devices-matter/ColorLightAccessory.d.ts.map +1 -1
  66. package/dist/devices-matter/ColorLightAccessory.js +12 -12
  67. package/dist/devices-matter/ColorLightAccessory.js.map +1 -1
  68. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts.map +1 -1
  69. package/dist/devices-matter/ColorTemperatureLightAccessory.js +5 -7
  70. package/dist/devices-matter/ColorTemperatureLightAccessory.js.map +1 -1
  71. package/dist/devices-matter/DimmableLightAccessory.js +9 -9
  72. package/dist/devices-matter/DimmableLightAccessory.js.map +1 -1
  73. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts.map +1 -1
  74. package/dist/devices-matter/ExtendedColorLightAccessory.js +14 -15
  75. package/dist/devices-matter/ExtendedColorLightAccessory.js.map +1 -1
  76. package/dist/devices-matter/OnOffLightAccessory.d.ts.map +1 -1
  77. package/dist/devices-matter/OnOffLightAccessory.js +8 -16
  78. package/dist/devices-matter/OnOffLightAccessory.js.map +1 -1
  79. package/dist/devices-matter/OnOffOutletAccessory.d.ts +2 -0
  80. package/dist/devices-matter/OnOffOutletAccessory.d.ts.map +1 -1
  81. package/dist/devices-matter/OnOffOutletAccessory.js +10 -7
  82. package/dist/devices-matter/OnOffOutletAccessory.js.map +1 -1
  83. package/dist/devices-matter/OnOffSwitchAccessory.js +2 -2
  84. package/dist/devices-matter/OnOffSwitchAccessory.js.map +1 -1
  85. package/dist/devices-matter/RoboticVacuumAccessory.d.ts +36 -43
  86. package/dist/devices-matter/RoboticVacuumAccessory.d.ts.map +1 -1
  87. package/dist/devices-matter/RoboticVacuumAccessory.js +478 -268
  88. package/dist/devices-matter/RoboticVacuumAccessory.js.map +1 -1
  89. package/dist/devices-matter/VenetianBlindAccessory.d.ts +6 -6
  90. package/dist/devices-matter/VenetianBlindAccessory.d.ts.map +1 -1
  91. package/dist/devices-matter/VenetianBlindAccessory.js.map +1 -1
  92. package/dist/devices-matter/WindowBlindAccessory.d.ts +5 -5
  93. package/dist/devices-matter/WindowBlindAccessory.d.ts.map +1 -1
  94. package/dist/devices-matter/WindowBlindAccessory.js +57 -6
  95. package/dist/devices-matter/WindowBlindAccessory.js.map +1 -1
  96. package/dist/homebridge-ui/public/index.html +219 -19
  97. package/dist/homebridge-ui/server.js +0 -31
  98. package/dist/homebridge-ui/server.js.map +1 -1
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +4 -9
  101. package/dist/index.js.map +1 -1
  102. package/dist/irdevice/irdevice.d.ts +11 -10
  103. package/dist/irdevice/irdevice.d.ts.map +1 -1
  104. package/dist/irdevice/irdevice.js +76 -35
  105. package/dist/irdevice/irdevice.js.map +1 -1
  106. package/dist/platform-hap.d.ts +26 -15
  107. package/dist/platform-hap.d.ts.map +1 -1
  108. package/dist/platform-hap.js +333 -153
  109. package/dist/platform-hap.js.map +1 -1
  110. package/dist/platform-matter.d.ts +93 -6
  111. package/dist/platform-matter.d.ts.map +1 -1
  112. package/dist/platform-matter.js +1878 -229
  113. package/dist/platform-matter.js.map +1 -1
  114. package/dist/settings.d.ts +75 -7
  115. package/dist/settings.d.ts.map +1 -1
  116. package/dist/settings.js.map +1 -1
  117. package/dist/test/apiRequestTracker.test.d.ts +2 -0
  118. package/dist/test/apiRequestTracker.test.d.ts.map +1 -0
  119. package/dist/test/apiRequestTracker.test.js +392 -0
  120. package/dist/test/apiRequestTracker.test.js.map +1 -0
  121. package/dist/test/hap/device-webhook-context.test.d.ts +2 -0
  122. package/dist/test/hap/device-webhook-context.test.d.ts.map +1 -0
  123. package/dist/test/hap/device-webhook-context.test.js +128 -0
  124. package/dist/test/hap/device-webhook-context.test.js.map +1 -0
  125. package/dist/test/hap/platform-hap.logging.test.d.ts +2 -0
  126. package/dist/test/hap/platform-hap.logging.test.d.ts.map +1 -0
  127. package/dist/test/hap/platform-hap.logging.test.js +33 -0
  128. package/dist/test/hap/platform-hap.logging.test.js.map +1 -0
  129. package/dist/test/hap/platform-hap.test.d.ts +2 -0
  130. package/dist/test/hap/platform-hap.test.d.ts.map +1 -0
  131. package/dist/test/hap/platform-hap.test.js +62 -0
  132. package/dist/test/hap/platform-hap.test.js.map +1 -0
  133. package/dist/test/helpers/platform-fixtures.d.ts +9 -0
  134. package/dist/test/helpers/platform-fixtures.d.ts.map +1 -0
  135. package/dist/test/helpers/platform-fixtures.js +30 -0
  136. package/dist/test/helpers/platform-fixtures.js.map +1 -0
  137. package/dist/test/homebridge-ui/server.test.d.ts +2 -0
  138. package/dist/test/homebridge-ui/server.test.d.ts.map +1 -0
  139. package/dist/test/homebridge-ui/server.test.js +445 -0
  140. package/dist/test/homebridge-ui/server.test.js.map +1 -0
  141. package/dist/{index.test.d.ts.map → test/index.test.d.ts.map} +1 -1
  142. package/dist/test/index.test.js +19 -0
  143. package/dist/test/index.test.js.map +1 -0
  144. package/dist/test/matter/devices-matter/baseMatterAccessory.test.d.ts +2 -0
  145. package/dist/test/matter/devices-matter/baseMatterAccessory.test.d.ts.map +1 -0
  146. package/dist/test/matter/devices-matter/baseMatterAccessory.test.js +71 -0
  147. package/dist/test/matter/devices-matter/baseMatterAccessory.test.js.map +1 -0
  148. package/dist/test/matter/platform-matter.additional.test.d.ts +2 -0
  149. package/dist/test/matter/platform-matter.additional.test.d.ts.map +1 -0
  150. package/dist/test/matter/platform-matter.additional.test.js +35 -0
  151. package/dist/test/matter/platform-matter.additional.test.js.map +1 -0
  152. package/dist/test/matter/platform-matter.bleparse.test.d.ts +2 -0
  153. package/dist/test/matter/platform-matter.bleparse.test.d.ts.map +1 -0
  154. package/dist/test/matter/platform-matter.bleparse.test.js +43 -0
  155. package/dist/test/matter/platform-matter.bleparse.test.js.map +1 -0
  156. package/dist/test/matter/platform-matter.cleanup.test.d.ts +2 -0
  157. package/dist/test/matter/platform-matter.cleanup.test.d.ts.map +1 -0
  158. package/dist/test/matter/platform-matter.cleanup.test.js +70 -0
  159. package/dist/test/matter/platform-matter.cleanup.test.js.map +1 -0
  160. package/dist/test/matter/platform-matter.keepstale.test.d.ts +2 -0
  161. package/dist/test/matter/platform-matter.keepstale.test.d.ts.map +1 -0
  162. package/dist/test/matter/platform-matter.keepstale.test.js +27 -0
  163. package/dist/test/matter/platform-matter.keepstale.test.js.map +1 -0
  164. package/dist/test/matter/platform-matter.logging.test.d.ts +2 -0
  165. package/dist/test/matter/platform-matter.logging.test.d.ts.map +1 -0
  166. package/dist/test/matter/platform-matter.logging.test.js +29 -0
  167. package/dist/test/matter/platform-matter.logging.test.js.map +1 -0
  168. package/dist/test/matter/platform-matter.mapping.test.d.ts +2 -0
  169. package/dist/test/matter/platform-matter.mapping.test.d.ts.map +1 -0
  170. package/dist/test/matter/platform-matter.mapping.test.js +43 -0
  171. package/dist/test/matter/platform-matter.mapping.test.js.map +1 -0
  172. package/dist/test/matter/platform-matter.openapi-mapping.test.d.ts +2 -0
  173. package/dist/test/matter/platform-matter.openapi-mapping.test.d.ts.map +1 -0
  174. package/dist/test/matter/platform-matter.openapi-mapping.test.js +84 -0
  175. package/dist/test/matter/platform-matter.openapi-mapping.test.js.map +1 -0
  176. package/dist/test/matter/platform-matter.test.d.ts +2 -0
  177. package/dist/test/matter/platform-matter.test.d.ts.map +1 -0
  178. package/dist/test/matter/platform-matter.test.js +117 -0
  179. package/dist/test/matter/platform-matter.test.js.map +1 -0
  180. package/dist/test/matter/platform-matter.unregister.test.d.ts +2 -0
  181. package/dist/test/matter/platform-matter.unregister.test.d.ts.map +1 -0
  182. package/dist/test/matter/platform-matter.unregister.test.js +30 -0
  183. package/dist/test/matter/platform-matter.unregister.test.js.map +1 -0
  184. package/dist/test/matter/platform-matter.webhook.test.d.ts +2 -0
  185. package/dist/test/matter/platform-matter.webhook.test.d.ts.map +1 -0
  186. package/dist/test/matter/platform-matter.webhook.test.js +46 -0
  187. package/dist/test/matter/platform-matter.webhook.test.js.map +1 -0
  188. package/dist/test/utils.test.d.ts +2 -0
  189. package/dist/test/utils.test.d.ts.map +1 -0
  190. package/dist/test/utils.test.js +95 -0
  191. package/dist/test/utils.test.js.map +1 -0
  192. package/dist/test/verifyconfig.test.d.ts.map +1 -0
  193. package/dist/{verifyconfig.test.js → test/verifyconfig.test.js} +2 -2
  194. package/dist/test/verifyconfig.test.js.map +1 -0
  195. package/dist/utils.d.ts +204 -3
  196. package/dist/utils.d.ts.map +1 -1
  197. package/dist/utils.js +713 -33
  198. package/dist/utils.js.map +1 -1
  199. package/docs/assets/highlight.css +14 -0
  200. package/docs/assets/main.js +2 -2
  201. package/docs/index.html +31 -2
  202. package/docs/variables/default.html +1 -1
  203. package/package.json +15 -15
  204. package/src/devices-hap/airpurifier.ts +11 -6
  205. package/src/devices-hap/blindtilt.ts +3 -3
  206. package/src/devices-hap/bot.ts +15 -5
  207. package/src/devices-hap/ceilinglight.ts +12 -7
  208. package/src/devices-hap/colorbulb.ts +46 -10
  209. package/src/devices-hap/contact.ts +3 -3
  210. package/src/devices-hap/curtain.ts +2 -2
  211. package/src/devices-hap/device.ts +149 -70
  212. package/src/devices-hap/fan.ts +11 -6
  213. package/src/devices-hap/hub.ts +6 -5
  214. package/src/devices-hap/humidifier.ts +97 -4
  215. package/src/devices-hap/iosensor.ts +36 -21
  216. package/src/devices-hap/lightstrip.ts +35 -8
  217. package/src/devices-hap/lock.ts +13 -6
  218. package/src/devices-hap/meter.ts +6 -5
  219. package/src/devices-hap/meterplus.ts +6 -5
  220. package/src/devices-hap/meterpro.ts +7 -6
  221. package/src/devices-hap/motion.ts +3 -3
  222. package/src/devices-hap/plug.ts +10 -6
  223. package/src/devices-hap/relayswitch.ts +3 -3
  224. package/src/devices-hap/robotvacuumcleaner.ts +12 -6
  225. package/src/devices-hap/waterdetector.ts +3 -3
  226. package/src/devices-matter/BaseMatterAccessory.ts +176 -5
  227. package/src/devices-matter/ColorLightAccessory.ts +12 -12
  228. package/src/devices-matter/ColorTemperatureLightAccessory.ts +5 -7
  229. package/src/devices-matter/DimmableLightAccessory.ts +9 -9
  230. package/src/devices-matter/ExtendedColorLightAccessory.ts +14 -15
  231. package/src/devices-matter/OnOffLightAccessory.ts +8 -16
  232. package/src/devices-matter/OnOffOutletAccessory.ts +12 -7
  233. package/src/devices-matter/OnOffSwitchAccessory.ts +2 -2
  234. package/src/devices-matter/RoboticVacuumAccessory.ts +529 -315
  235. package/src/devices-matter/VenetianBlindAccessory.ts +5 -5
  236. package/src/devices-matter/WindowBlindAccessory.ts +53 -10
  237. package/src/homebridge-ui/public/index.html +219 -19
  238. package/src/homebridge-ui/server.ts +0 -34
  239. package/src/index.ts +4 -10
  240. package/src/irdevice/irdevice.ts +74 -35
  241. package/src/platform-hap.ts +365 -169
  242. package/src/platform-matter.ts +1923 -230
  243. package/src/settings.ts +78 -3
  244. package/src/test/apiRequestTracker.test.ts +417 -0
  245. package/src/test/hap/device-webhook-context.test.ts +136 -0
  246. package/src/test/hap/platform-hap.logging.test.ts +36 -0
  247. package/src/test/hap/platform-hap.test.ts +70 -0
  248. package/src/test/helpers/platform-fixtures.ts +33 -0
  249. package/src/test/homebridge-ui/server.test.ts +486 -0
  250. package/src/test/index.test.ts +24 -0
  251. package/src/test/matter/devices-matter/baseMatterAccessory.test.ts +88 -0
  252. package/src/test/matter/platform-matter.additional.test.ts +44 -0
  253. package/src/test/matter/platform-matter.bleparse.test.ts +47 -0
  254. package/src/test/matter/platform-matter.cleanup.test.ts +86 -0
  255. package/src/test/matter/platform-matter.keepstale.test.ts +37 -0
  256. package/src/test/matter/platform-matter.logging.test.ts +33 -0
  257. package/src/test/matter/platform-matter.mapping.test.ts +57 -0
  258. package/src/test/matter/platform-matter.openapi-mapping.test.ts +109 -0
  259. package/src/test/matter/platform-matter.test.ts +144 -0
  260. package/src/test/matter/platform-matter.unregister.test.ts +39 -0
  261. package/src/test/matter/platform-matter.webhook.test.ts +54 -0
  262. package/src/test/utils.test.ts +96 -0
  263. package/src/{verifyconfig.test.ts → test/verifyconfig.test.ts} +12 -11
  264. package/src/utils.ts +777 -36
  265. package/dist/index.test.js +0 -14
  266. package/dist/index.test.js.map +0 -1
  267. package/dist/verifyconfig.test.d.ts.map +0 -1
  268. package/dist/verifyconfig.test.js.map +0 -1
  269. package/src/index.test.ts +0 -19
  270. /package/dist/{index.test.d.ts → test/index.test.d.ts} +0 -0
  271. /package/dist/{verifyconfig.test.d.ts → test/verifyconfig.test.d.ts} +0 -0
@@ -1,75 +1,93 @@
1
- /* global NodeJS */
1
+ //
2
2
  import { BaseMatterAccessory } from './BaseMatterAccessory.js';
3
+ function detectModelFromContext(ctx) {
4
+ const t = ctx?.deviceType?.trim();
5
+ switch (t) {
6
+ case 'Robot Vacuum Cleaner S1':
7
+ return 'S1';
8
+ case 'Robot Vacuum Cleaner S1 Plus':
9
+ return 'S1 Plus';
10
+ case 'Robot Vacuum Cleaner S1 Pro':
11
+ return 'S1 Pro';
12
+ case 'Robot Vacuum Cleaner S1 Mini':
13
+ return 'S1 Mini';
14
+ case 'WoSweeper':
15
+ return 'WoSweeper';
16
+ case 'WoSweeperMini':
17
+ return 'WoSweeperMini';
18
+ case 'K10+':
19
+ return 'K10+';
20
+ case 'K10+ Pro':
21
+ return 'K10+ Pro';
22
+ case 'Robot Vacuum Cleaner K10+ Pro Combo':
23
+ case 'K10+ Pro Combo':
24
+ return 'K10+ Pro Combo';
25
+ case 'Robot Vacuum Cleaner S10':
26
+ return 'S10';
27
+ case 'Robot Vacuum Cleaner S20':
28
+ case 'S20':
29
+ return 'S20';
30
+ case 'Robot Vacuum Cleaner K11+':
31
+ case 'K11+':
32
+ return 'K11+';
33
+ case 'Robot Vacuum Cleaner K20 Plus Pro':
34
+ case 'K20+ Pro':
35
+ return 'K20+ Pro';
36
+ default:
37
+ return undefined;
38
+ }
39
+ }
40
+ function capabilitiesFor(model) {
41
+ // Defaults: conservative basic vac
42
+ if (!model) {
43
+ return { pause: false, resume: false, cleanAction: 'vacuum-only', suctionKind: 'powLevel-0-3', waterLevel: false };
44
+ }
45
+ // Basic vacuum models (S1 family, WoSweeper family, K10 family without Combo)
46
+ if (model === 'S1' || model === 'S1 Plus' || model === 'S1 Pro' || model === 'S1 Mini'
47
+ || model === 'WoSweeper' || model === 'WoSweeperMini'
48
+ || model === 'K10+' || model === 'K10+ Pro') {
49
+ return { pause: false, resume: false, cleanAction: 'vacuum-only', suctionKind: 'powLevel-0-3', waterLevel: false };
50
+ }
51
+ // K10+ Pro Combo: vacuum or mop, fanLevel, no waterLevel
52
+ if (model === 'K10+ Pro Combo') {
53
+ return { pause: true, resume: false, cleanAction: 'vacuum-or-mop', suctionKind: 'fanLevel-1-4', waterLevel: false };
54
+ }
55
+ // S10/S20: vacuum or vacuum+mop combo, fanLevel, waterLevel, plus advanced commands
56
+ if (model === 'S10' || model === 'S20') {
57
+ return {
58
+ pause: true,
59
+ resume: false,
60
+ cleanAction: 'vacuum-or-vacmop',
61
+ suctionKind: 'fanLevel-1-4',
62
+ waterLevel: true,
63
+ advancedCommands: {
64
+ setVolume: true,
65
+ selfClean: true,
66
+ addWaterForHumi: true,
67
+ },
68
+ };
69
+ }
70
+ // K11+/K20+ Pro: vacuum or mop, fanLevel, waterLevel, volume control
71
+ return {
72
+ pause: true,
73
+ resume: false,
74
+ cleanAction: 'vacuum-or-mop',
75
+ suctionKind: 'fanLevel-1-4',
76
+ waterLevel: true,
77
+ advancedCommands: {
78
+ setVolume: true,
79
+ },
80
+ };
81
+ }
3
82
  export class RoboticVacuumAccessory extends BaseMatterAccessory {
4
- activeTimers = [];
83
+ // Track current high-level preferences that affect OpenAPI payloads
84
+ currentCleanAction = 'vacuum';
85
+ currentFanLevel = 2;
86
+ capabilities;
5
87
  constructor(api, log, opts) {
6
88
  const serialNumber = opts?.serialNumber ?? 'VACUUM-001';
7
- const clusters = opts?.clusters ?? {
8
- powerSource: {
9
- status: 0,
10
- order: 0,
11
- description: 'Battery',
12
- batPercentRemaining: 100,
13
- batChargeLevel: 2,
14
- batReplaceability: 1,
15
- },
16
- rvcRunMode: {
17
- supportedModes: [
18
- { label: 'Idle', mode: 0, modeTags: [{ value: 16384 }] },
19
- { label: 'Cleaning', mode: 1, modeTags: [{ value: 16385 }] },
20
- { label: 'Mapping', mode: 2, modeTags: [{ value: 16386 }] },
21
- ],
22
- currentMode: 0,
23
- },
24
- rvcCleanMode: {
25
- supportedModes: [
26
- { label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
27
- { label: 'Mop', mode: 1, modeTags: [{ value: 16386 }] },
28
- { label: 'Vacuum & Mop', mode: 2, modeTags: [{ value: 16385 }, { value: 16386 }] },
29
- { label: 'Deep Clean', mode: 3, modeTags: [{ value: 16384 }] },
30
- { label: 'Deep Vacuum', mode: 4, modeTags: [{ value: 16384 }, { value: 16385 }] },
31
- { label: 'Deep Mop', mode: 5, modeTags: [{ value: 16384 }, { value: 16386 }] },
32
- { label: 'Quick Clean', mode: 6, modeTags: [{ value: 1 }, { value: 16385 }] },
33
- { label: 'Max Clean', mode: 7, modeTags: [{ value: 7 }, { value: 16385 }] },
34
- { label: 'Min Clean', mode: 8, modeTags: [{ value: 6 }, { value: 16385 }] },
35
- { label: 'Quiet Vacuum', mode: 9, modeTags: [{ value: 2 }, { value: 16385 }] },
36
- { label: 'Quiet Mop', mode: 10, modeTags: [{ value: 2 }, { value: 16386 }] },
37
- { label: 'Night Mode', mode: 11, modeTags: [{ value: 8 }, { value: 16385 }] },
38
- { label: 'Eco Vacuum', mode: 12, modeTags: [{ value: 4 }, { value: 16385 }] },
39
- { label: 'Eco Mop', mode: 13, modeTags: [{ value: 4 }, { value: 16386 }] },
40
- { label: 'Auto', mode: 14, modeTags: [{ value: 0 }, { value: 16385 }] },
41
- ],
42
- currentMode: 0,
43
- },
44
- rvcOperationalState: {
45
- operationalStateList: [
46
- { operationalStateId: 0 },
47
- { operationalStateId: 1 },
48
- { operationalStateId: 2 },
49
- { operationalStateId: 3 },
50
- { operationalStateId: 64 },
51
- { operationalStateId: 65 },
52
- { operationalStateId: 66 },
53
- ],
54
- operationalState: 66,
55
- },
56
- serviceArea: {
57
- supportedMaps: [],
58
- supportedAreas: [
59
- { areaId: 0, mapId: null, areaInfo: { locationInfo: { locationName: 'Living Room', floorNumber: 0, areaType: 7 }, landmarkInfo: null } },
60
- { areaId: 1, mapId: null, areaInfo: { locationInfo: { locationName: 'Kitchen', floorNumber: 0, areaType: 10 }, landmarkInfo: null } },
61
- { areaId: 2, mapId: null, areaInfo: { locationInfo: { locationName: 'Bedroom', floorNumber: 0, areaType: 2 }, landmarkInfo: null } },
62
- { areaId: 3, mapId: null, areaInfo: { locationInfo: { locationName: 'Bathroom', floorNumber: 0, areaType: 6 }, landmarkInfo: null } },
63
- ],
64
- selectedAreas: [0, 1, 2, 3],
65
- },
66
- };
67
- const handlers = opts?.handlers ?? {
68
- rvcRunMode: { changeToMode: async (request) => this.handleChangeRunMode(request) },
69
- rvcCleanMode: { changeToMode: async (request) => this.handleChangeCleanMode(request) },
70
- rvcOperationalState: { pause: async () => this.handlePause(), stop: async () => this.handleStop(), start: async () => this.handleStart(), resume: async () => this.handleResume(), goHome: async () => this.handleGoHome() },
71
- serviceArea: { selectAreas: async (request) => this.handleSelectAreas(request), skipArea: async (request) => this.handleSkipArea(request) },
72
- };
89
+ const model = detectModelFromContext(opts?.context);
90
+ const capabilities = capabilitiesFor(model);
73
91
  super(api, log, {
74
92
  uuid: opts?.uuid ?? api.matter.uuid.generate(serialNumber),
75
93
  displayName: opts?.displayName ?? 'Robot Vacuum',
@@ -79,188 +97,331 @@ export class RoboticVacuumAccessory extends BaseMatterAccessory {
79
97
  model: opts?.model ?? 'HB-MATTER-VACUUM-ROBOTIC',
80
98
  firmwareRevision: opts?.firmwareRevision ?? '2.0.0',
81
99
  hardwareRevision: opts?.hardwareRevision ?? '1.0.0',
82
- clusters,
83
- handlers,
84
- context: { deviceId: opts?.deviceId, ...(opts?.context ?? {}) },
100
+ clusters: {
101
+ rvcRunMode: {
102
+ supportedModes: [
103
+ { label: 'Idle', mode: 0, modeTags: [{ value: 16384 }] },
104
+ { label: 'Cleaning', mode: 1, modeTags: [{ value: 16385 }] },
105
+ ],
106
+ currentMode: 0,
107
+ },
108
+ rvcCleanMode: (() => {
109
+ switch (model) {
110
+ case 'K10':
111
+ return {
112
+ supportedModes: [
113
+ { label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
114
+ ],
115
+ currentMode: 0,
116
+ };
117
+ case 'Robot Vacuum Cleaner S1':
118
+ case 'Robot Vacuum Cleaner S1 Plus':
119
+ return {
120
+ supportedModes: [
121
+ { label: 'Quiet', mode: 0, modeTags: [{ value: 2 }] },
122
+ { label: 'Standard', mode: 1, modeTags: [{ value: 16384 }] },
123
+ { label: 'Strong', mode: 2, modeTags: [{ value: 7 }] },
124
+ { label: 'MAX', mode: 3, modeTags: [{ value: 8 }] },
125
+ ],
126
+ currentMode: 1,
127
+ };
128
+ case 'Mini Robot Vacuum K10+':
129
+ case 'Mini Robot Vacuum K10+ Pro':
130
+ case 'K10+ Pro Combo':
131
+ return {
132
+ supportedModes: [
133
+ { label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
134
+ { label: 'Mop', mode: 1, modeTags: [{ value: 16386 }] },
135
+ { label: 'Sweep & Mop', mode: 2, modeTags: [{ value: 16385 }, { value: 16386 }] },
136
+ ],
137
+ currentMode: 0,
138
+ };
139
+ case 'Multitasking Household Robot K20+ Pro':
140
+ return {
141
+ supportedModes: [
142
+ { label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
143
+ { label: 'Vacuum & Mop', mode: 1, modeTags: [{ value: 16385 }, { value: 16386 }] },
144
+ { label: 'Deep Clean', mode: 2, modeTags: [{ value: 16384 }] },
145
+ ],
146
+ currentMode: 0,
147
+ };
148
+ case 'Floor Cleaning Robot S10':
149
+ case 'Floor Cleaning Robot S20':
150
+ return {
151
+ supportedModes: [
152
+ { label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
153
+ { label: 'Mop', mode: 1, modeTags: [{ value: 16386 }] },
154
+ ],
155
+ currentMode: 0,
156
+ };
157
+ case 'Robot Vacuum K11+':
158
+ return {
159
+ supportedModes: [
160
+ { label: 'Quiet', mode: 0, modeTags: [{ value: 2 }] },
161
+ { label: 'Standard', mode: 1, modeTags: [{ value: 16384 }] },
162
+ { label: 'Strong', mode: 2, modeTags: [{ value: 7 }] },
163
+ { label: 'MAX', mode: 3, modeTags: [{ value: 8 }] },
164
+ { label: 'Deep Clean', mode: 4, modeTags: [{ value: 16384 }, { value: 16385 }] },
165
+ ],
166
+ currentMode: 1,
167
+ };
168
+ default:
169
+ return {
170
+ supportedModes: [
171
+ { label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
172
+ { label: 'Vacuum & Mop', mode: 1, modeTags: [{ value: 16385 }, { value: 16386 }] },
173
+ { label: 'Deep Clean', mode: 2, modeTags: [{ value: 16384 }] },
174
+ ],
175
+ currentMode: 0,
176
+ };
177
+ }
178
+ })(),
179
+ rvcOperationalState: {
180
+ operationalStateList: [
181
+ { operationalStateId: 0 }, // Stopped
182
+ { operationalStateId: 1 }, // Running
183
+ { operationalStateId: 2 }, // Paused
184
+ { operationalStateId: 3 }, // Error
185
+ { operationalStateId: 64 }, // Seeking Charger
186
+ { operationalStateId: 65 }, // Charging
187
+ { operationalStateId: 66 }, // Docked
188
+ { operationalStateId: 67 }, // In Remote Control
189
+ { operationalStateId: 68 }, // In Dust Collecting
190
+ ],
191
+ operationalState: 66,
192
+ },
193
+ },
194
+ handlers: (() => {
195
+ const opHandlers = {
196
+ stop: async () => this.handleStop(),
197
+ start: async () => this.handleStart(),
198
+ goHome: async () => this.handleGoHome(),
199
+ // Always register resume handler, log warning if not supported
200
+ resume: async () => {
201
+ if (capabilities.resume) {
202
+ await this.handleResume();
203
+ }
204
+ else {
205
+ this.logWarn(`Resume operation is not supported for model: ${this.model}`);
206
+ }
207
+ },
208
+ };
209
+ if (capabilities.pause) {
210
+ opHandlers.pause = async () => this.handlePause();
211
+ }
212
+ return {
213
+ rvcRunMode: { changeToMode: async (request) => this.handleChangeRunMode(request) },
214
+ rvcCleanMode: { changeToMode: async (request) => this.handleChangeCleanMode(request) },
215
+ rvcOperationalState: opHandlers,
216
+ };
217
+ })(),
218
+ context: { deviceId: opts?.deviceId, ...(opts?.context ?? {}), deviceType: opts?.context?.deviceType },
85
219
  });
220
+ this.capabilities = capabilities;
86
221
  this.logInfo('initialized and ready.');
87
222
  }
88
223
  async handleChangeRunMode(request) {
89
224
  this.logInfo(`ChangeToMode (run) request received: ${JSON.stringify(request)}`);
90
225
  const { newMode } = request;
91
- const modeStr = ['Idle', 'Cleaning', 'Mapping'][newMode] || `Unknown (mode=${newMode})`;
92
- this.logInfo(`changing run mode to: ${modeStr}`);
93
- // TODO: await myVacuumAPI.setRunMode(newMode)
94
- // Clear any existing timers
95
- this.clearTimers();
96
- if (newMode === 1) {
97
- // Switching to Cleaning mode - start the vacuum
98
- this.updateOperationalState(1); // Running
99
- // Simulate cleaning completion after 15 seconds
100
- const completionTimer = setTimeout(() => {
101
- this.logInfo('cleaning completed, returning to dock.');
102
- this.updateRunMode(0); // Set to Idle - cleaning session ending
103
- this.returnToDock();
104
- }, 15000);
105
- this.activeTimers.push(completionTimer);
106
- }
107
- else if (newMode === 0) {
108
- // Switching to Idle mode - return to dock
109
- this.returnToDock();
110
- }
111
- else if (newMode === 2) {
112
- // Switching to Mapping mode - start mapping
113
- this.updateOperationalState(1); // Running
114
- // Simulate mapping completion after 20 seconds
115
- const completionTimer = setTimeout(() => {
116
- this.logInfo('mapping completed, returning to dock.');
117
- this.returnToDock();
118
- }, 20000);
119
- this.activeTimers.push(completionTimer);
226
+ const modeStr = ['Idle', 'Cleaning', 'Returning to Dock'][newMode] || `Unknown (mode=${newMode})`;
227
+ this.logInfo(`Changing run mode to: ${modeStr}`);
228
+ switch (this.model) {
229
+ case 'Robot Vacuum Cleaner S1':
230
+ case 'Robot Vacuum Cleaner S1 Plus': {
231
+ if (newMode === 1) {
232
+ await this.handleStart();
233
+ }
234
+ else if (newMode === 0) {
235
+ await this.handleGoHome();
236
+ }
237
+ break;
238
+ }
239
+ case 'Mini Robot Vacuum K10+':
240
+ case 'Mini Robot Vacuum K10+ Pro':
241
+ case 'K10+ Pro Combo': {
242
+ if (newMode === 1) {
243
+ await this.handleStart();
244
+ }
245
+ else if (newMode === 2) {
246
+ await this.handleDock();
247
+ }
248
+ break;
249
+ }
250
+ case 'Multitasking Household Robot K20+ Pro': {
251
+ if (newMode === 1) {
252
+ await this.handleStart();
253
+ }
254
+ else if (newMode === 0) {
255
+ await this.handleGoHome();
256
+ }
257
+ else if (newMode === 2) {
258
+ await this.handlePause();
259
+ }
260
+ break;
261
+ }
262
+ case 'Floor Cleaning Robot S10':
263
+ case 'Floor Cleaning Robot S20': {
264
+ if (newMode === 1) {
265
+ await this.handleStart();
266
+ }
267
+ else if (newMode === 0) {
268
+ await this.handleGoHome();
269
+ }
270
+ break;
271
+ }
272
+ case 'Robot Vacuum K11+': {
273
+ if (newMode === 1) {
274
+ await this.handleStart();
275
+ }
276
+ else if (newMode === 0) {
277
+ await this.handleGoHome();
278
+ }
279
+ else if (newMode === 2) {
280
+ await this.handleDock();
281
+ }
282
+ break;
283
+ }
284
+ default:
285
+ this.logWarn(`Run mode change not supported for model: ${this.model}`);
120
286
  }
121
287
  }
122
288
  async handleChangeCleanMode(request) {
123
289
  this.logInfo(`ChangeToMode (clean) request received: ${JSON.stringify(request)}`);
124
290
  const { newMode } = request;
125
- const modes = [
126
- 'Vacuum',
127
- 'Mop',
128
- 'Vacuum & Mop',
129
- 'Deep Clean',
130
- 'Deep Vacuum',
131
- 'Deep Mop',
132
- 'Quick Clean',
133
- 'Max Clean',
134
- 'Min Clean',
135
- 'Quiet Vacuum',
136
- 'Quiet Mop',
137
- 'Night Mode',
138
- 'Eco Vacuum',
139
- 'Eco Mop',
140
- 'Auto',
141
- ];
142
- const modeStr = modes[newMode] || `Unknown (mode=${newMode})`;
143
- this.logInfo(`changing clean mode to: ${modeStr}`);
144
- // TODO: await myVacuumAPI.setCleanMode(newMode)
291
+ switch (this.model) {
292
+ case 'Robot Vacuum Cleaner S1':
293
+ case 'Robot Vacuum Cleaner S1 Plus': {
294
+ const mapPowS1 = ['Quiet', 'Standard', 'Strong', 'MAX'];
295
+ const labelS1 = mapPowS1[newMode] ?? `Unknown (${newMode})`;
296
+ this.logInfo(`Changing suction level to: ${labelS1}`);
297
+ await this.sendOpenAPICommand('PowLevel', String(newMode));
298
+ break;
299
+ }
300
+ case 'Mini Robot Vacuum K10+':
301
+ case 'Mini Robot Vacuum K10+ Pro':
302
+ case 'K10+ Pro Combo': {
303
+ const mapPowK10 = ['Vacuum', 'Mop', 'Sweep & Mop'];
304
+ const labelK10 = mapPowK10[newMode] ?? `Unknown (${newMode})`;
305
+ this.logInfo(`Changing cleaning mode to: ${labelK10}`);
306
+ await this.sendOpenAPICommand('CleanMode', String(newMode));
307
+ break;
308
+ }
309
+ case 'Multitasking Household Robot K20+ Pro': {
310
+ const mapPowK20 = ['Vacuum', 'Vacuum & Mop', 'Deep Clean'];
311
+ const labelK20 = mapPowK20[newMode] ?? `Unknown (${newMode})`;
312
+ this.logInfo(`Changing cleaning mode to: ${labelK20}`);
313
+ await this.sendOpenAPICommand('CleanMode', String(newMode));
314
+ break;
315
+ }
316
+ case 'Floor Cleaning Robot S10':
317
+ case 'Floor Cleaning Robot S20': {
318
+ const mapPowS10 = ['Vacuum', 'Mop'];
319
+ const labelS10 = mapPowS10[newMode] ?? `Unknown (${newMode})`;
320
+ this.logInfo(`Changing cleaning mode to: ${labelS10}`);
321
+ await this.sendOpenAPICommand('CleanMode', String(newMode));
322
+ break;
323
+ }
324
+ case 'Robot Vacuum K11+': {
325
+ const mapPowK11 = ['Quiet', 'Standard', 'Strong', 'MAX', 'Deep Clean'];
326
+ const labelK11 = mapPowK11[newMode] ?? `Unknown (${newMode})`;
327
+ this.logInfo(`Changing suction level to: ${labelK11}`);
328
+ await this.sendOpenAPICommand('PowLevel', String(newMode));
329
+ break;
330
+ }
331
+ default:
332
+ this.logWarn(`Clean mode change not supported for model: ${this.model}`);
333
+ }
145
334
  }
146
335
  async handlePause() {
147
336
  this.logInfo('pausing.');
148
- // TODO: await myVacuumAPI.pause()
149
- this.clearTimers(); // Clear cleaning completion timer
150
- this.updateOperationalState(2); // Paused
337
+ try {
338
+ this.logInfo(`[OpenAPI] Sending pause command`);
339
+ await this.sendOpenAPICommand('pause');
340
+ this.logInfo(`[OpenAPI] pause command sent`);
341
+ }
342
+ catch (e) {
343
+ this.logWarn(`OpenAPI pause failed: ${String(e?.message ?? e)}`);
344
+ }
345
+ this.updateOperationalState(2);
151
346
  }
152
347
  async handleStop() {
153
348
  this.logInfo('stopping.');
154
- // TODO: await myVacuumAPI.stop()
155
- this.clearTimers();
156
- this.updateRunMode(0); // Reset to Idle
157
- this.updateOperationalState(0); // Stopped
349
+ try {
350
+ if (this.capabilities.cleanAction === 'vacuum-only' && this.capabilities.suctionKind === 'powLevel-0-3') {
351
+ this.logInfo(`[OpenAPI] Sending stop command`);
352
+ await this.sendOpenAPICommand('stop');
353
+ this.logInfo(`[OpenAPI] stop command sent`);
354
+ }
355
+ else {
356
+ this.logInfo(`[OpenAPI] Sending pause command (for stop)`);
357
+ await this.sendOpenAPICommand('pause');
358
+ this.logInfo(`[OpenAPI] pause command sent (for stop)`);
359
+ }
360
+ }
361
+ catch (e) {
362
+ this.logWarn(`OpenAPI stop/pause failed: ${String(e?.message ?? e)}`);
363
+ }
364
+ this.updateRunMode(0);
365
+ this.updateOperationalState(0);
158
366
  }
159
367
  async handleStart() {
160
- this.logInfo('starting (via start command).');
161
- // TODO: await myVacuumAPI.start()
162
- // Clear any existing timers
163
- this.clearTimers();
164
- this.updateRunMode(1); // Set to Cleaning mode - this will trigger the run mode handler logic
165
- this.updateOperationalState(1); // Running
166
- // Simulate cleaning completion after 15 seconds
167
- const completionTimer = setTimeout(() => {
168
- this.logInfo('cleaning completed, returning to dock.');
169
- this.updateRunMode(0); // Set to Idle - cleaning session ending
170
- this.returnToDock();
171
- }, 15000);
172
- this.activeTimers.push(completionTimer);
368
+ this.logInfo('starting clean.');
369
+ try {
370
+ if (this.capabilities.cleanAction === 'vacuum-only' && this.capabilities.suctionKind === 'powLevel-0-3') {
371
+ this.logInfo(`[OpenAPI] Sending start command`);
372
+ await this.sendOpenAPICommand('start');
373
+ this.logInfo(`[OpenAPI] start command sent`);
374
+ }
375
+ else {
376
+ const action = this.currentCleanAction === 'vacuum_mop' ? 'sweep_mop' : this.currentCleanAction;
377
+ const param = { action, param: { fanLevel: this.currentFanLevel } };
378
+ if (this.capabilities.waterLevel) {
379
+ param.param.waterLevel = 1;
380
+ }
381
+ this.logInfo(`[OpenAPI] Sending startClean: ${JSON.stringify(param)}`);
382
+ await this.sendOpenAPICommand('startClean', JSON.stringify(param));
383
+ this.logInfo(`[OpenAPI] startClean command sent: ${JSON.stringify(param)}`);
384
+ }
385
+ }
386
+ catch (e) {
387
+ this.logWarn(`OpenAPI start/startClean failed: ${String(e?.message ?? e)}`);
388
+ }
389
+ this.updateRunMode(1);
390
+ this.updateOperationalState(1);
173
391
  }
174
392
  async handleResume() {
175
- this.logInfo('resuming.');
176
- // TODO: await myVacuumAPI.resume()
177
- // Clear any existing timers
178
- this.clearTimers();
179
- this.updateRunMode(1); // Set to Cleaning mode
180
- this.updateOperationalState(1); // Running
181
- // Simulate cleaning completion after 10 seconds (shorter since resuming)
182
- const completionTimer = setTimeout(() => {
183
- this.logInfo('cleaning completed, returning to dock.');
184
- this.updateRunMode(0); // Set to Idle - cleaning session ending
185
- this.returnToDock();
186
- }, 10000);
187
- this.activeTimers.push(completionTimer);
393
+ this.logInfo('resume requested.');
394
+ try {
395
+ this.logInfo(`[OpenAPI] Sending resume command`);
396
+ await this.sendOpenAPICommand('resume');
397
+ this.logInfo(`[OpenAPI] resume command sent`);
398
+ }
399
+ catch (e) {
400
+ this.logWarn(`OpenAPI resume failed: ${String(e?.message ?? e)}`);
401
+ }
402
+ this.updateRunMode(1);
403
+ this.updateOperationalState(1);
188
404
  }
189
405
  async handleGoHome() {
190
- this.logInfo('goHome command received.');
191
- // TODO: await myVacuumAPI.goHome()
192
- // Clear any existing timers
193
- this.clearTimers();
194
- // Defer state updates to ensure handler completes first
195
- setImmediate(() => {
196
- // Set to Idle mode since we're ending the cleaning session
197
- this.updateRunMode(0);
198
- // Initiate return to dock sequence
199
- this.returnToDock();
200
- });
201
- }
202
- async handleSelectAreas(request) {
203
- this.logInfo(`SelectAreas request received: ${JSON.stringify(request)}`);
204
- const { newAreas } = request;
205
- const areaNames = newAreas.map((id) => ['Living Room', 'Kitchen', 'Bedroom', 'Bathroom'][id] || `Area ${id}`);
206
- this.logInfo(`selecting areas: ${areaNames.join(', ')}`);
207
- // TODO: await myVacuumAPI.selectAreas(newAreas)
208
- }
209
- async handleSkipArea(request) {
210
- this.logInfo(`SkipArea request received: ${JSON.stringify(request)}`);
211
- const { skippedArea } = request;
212
- const areaName = ['Living Room', 'Kitchen', 'Bedroom', 'Bathroom'][skippedArea] || `Area ${skippedArea}`;
213
- this.logInfo(`skipping area: ${areaName}`);
214
- // TODO: await myVacuumAPI.skipArea(skippedArea)
215
- }
216
- /**
217
- * Helper method to clear all active timers
218
- */
219
- clearTimers() {
220
- this.activeTimers.forEach(timer => clearTimeout(timer));
221
- this.activeTimers = [];
222
- }
223
- /**
224
- * Helper method to initiate return to dock sequence
225
- * Can be called synchronously from other handlers
226
- */
227
- returnToDock() {
228
- this.logInfo('initiating return to dock sequence.');
229
- // Defer ALL state updates to ensure handler completes first
230
- setImmediate(() => {
231
- // Start seeking charger directly (skip intermediate Stopped state)
232
- this.updateOperationalState(64); // Seeking Charger
233
- // After 5 seconds, start charging
234
- const chargingTimer = setTimeout(() => {
235
- this.logInfo('reached dock, now charging.');
236
- this.updateOperationalState(65); // Charging
237
- // After 3 more seconds, fully docked
238
- const dockedTimer = setTimeout(() => {
239
- this.logInfo('fully charged and docked.');
240
- this.updateOperationalState(66); // Docked
241
- }, 3000);
242
- this.activeTimers.push(dockedTimer);
243
- }, 5000);
244
- this.activeTimers.push(chargingTimer);
245
- });
406
+ this.logInfo('returning to dock.');
407
+ try {
408
+ this.logInfo(`[OpenAPI] Sending dock command`);
409
+ await this.sendOpenAPICommand('dock');
410
+ this.logInfo(`[OpenAPI] dock command sent`);
411
+ }
412
+ catch (e) {
413
+ this.logWarn(`OpenAPI dock failed: ${String(e?.message ?? e)}`);
414
+ }
415
+ this.updateRunMode(0);
416
+ this.updateOperationalState(64);
246
417
  }
247
- /**
248
- * Update Methods - Use these to update the vacuum state from your API/device
249
- *
250
- * Since this is a platform accessory, state updates work immediately after registration.
251
- */
252
418
  updateOperationalState(state) {
253
419
  this.updateState('rvcOperationalState', { operationalState: state });
254
420
  const states = [
255
421
  'Stopped',
256
422
  'Running',
257
423
  'Paused',
258
- 'Error',
259
- null,
260
- null,
261
- null,
262
- null,
263
- ...Array.from({ length: 56 }).fill(null),
424
+ ...Array.from({ length: 61 }).fill(null),
264
425
  'Seeking Charger',
265
426
  'Charging',
266
427
  'Docked',
@@ -269,66 +430,115 @@ export class RoboticVacuumAccessory extends BaseMatterAccessory {
269
430
  }
270
431
  updateRunMode(mode) {
271
432
  this.updateState('rvcRunMode', { currentMode: mode });
272
- const modes = ['Idle', 'Cleaning', 'Mapping'];
433
+ const modes = ['Idle', 'Cleaning'];
273
434
  this.logInfo(`run mode updated to: ${modes[mode] || `Unknown (${mode})`}`);
274
435
  }
275
436
  updateCleanMode(mode) {
276
437
  this.updateState('rvcCleanMode', { currentMode: mode });
277
- const modes = [
278
- 'Vacuum',
279
- 'Mop',
280
- 'Vacuum & Mop',
281
- 'Deep Clean',
282
- 'Deep Vacuum',
283
- 'Deep Mop',
284
- 'Quick Clean',
285
- 'Max Clean',
286
- 'Min Clean',
287
- 'Quiet Vacuum',
288
- 'Quiet Mop',
289
- 'Night Mode',
290
- 'Eco Vacuum',
291
- 'Eco Mop',
292
- 'Auto',
293
- ];
294
- this.logInfo(`clean mode updated to: ${modes[mode] || `Unknown (${mode})`}`);
438
+ const label = mode === 0 ? 'Vacuum' : mode === 1 ? 'Mop' : mode === 2 ? 'Vacuum & Mop' : 'Unknown';
439
+ this.logInfo(`clean mode updated to: ${label}`);
295
440
  }
296
- updateSelectedAreas(areaIds) {
297
- this.updateState('serviceArea', { selectedAreas: areaIds });
298
- const areaNames = areaIds.map(id => ['Living Room', 'Kitchen', 'Bedroom', 'Bathroom'][id] || `Area ${id}`);
299
- this.logInfo(`selected areas updated to: ${areaNames.join(', ') || 'All Areas'}`);
441
+ /**
442
+ * Update battery percentage (no PowerSource cluster for this device type)
443
+ */
444
+ async updateBatteryPercentage(percentageOrBatPercentRemaining) {
445
+ const isBatPercent = Number(percentageOrBatPercentRemaining) > 100;
446
+ const percentage = isBatPercent
447
+ ? Math.max(0, Math.min(100, Math.round(Number(percentageOrBatPercentRemaining) / 2)))
448
+ : Math.max(0, Math.min(100, Math.round(Number(percentageOrBatPercentRemaining))));
449
+ let chargeLevel = 0; // Ok
450
+ if (percentage < 20) {
451
+ chargeLevel = 2; // Critical
452
+ }
453
+ else if (percentage < 40) {
454
+ chargeLevel = 1; // Warning
455
+ }
456
+ this.logInfo(`battery status: ${percentage}% (${chargeLevel === 0 ? 'Ok' : chargeLevel === 1 ? 'Warning' : 'Critical'})`);
457
+ if (this.context) {
458
+ this.context.batteryPercentage = percentage;
459
+ this.context.batteryChargeLevel = chargeLevel;
460
+ }
300
461
  }
301
- updateCurrentArea(areaId) {
302
- this.updateState('serviceArea', { currentArea: areaId });
303
- if (areaId !== null) {
304
- const areaName = ['Living Room', 'Kitchen', 'Bedroom', 'Bathroom'][areaId] || `Area ${areaId}`;
305
- this.logInfo(`current area updated to: ${areaName}`);
462
+ async handleDock() {
463
+ this.logInfo('Docking the vacuum.');
464
+ try {
465
+ this.logInfo('[OpenAPI] Sending dock command');
466
+ await this.sendOpenAPICommand('dock');
467
+ this.logInfo('[OpenAPI] Dock command sent successfully');
468
+ this.updateRunMode(0); // Set to Idle after docking
469
+ this.updateOperationalState(64); // Seeking Charger state
306
470
  }
307
- else {
308
- this.logInfo('current area cleared');
471
+ catch (error) {
472
+ this.logWarn(`Docking failed: ${String(error?.message ?? error)}`);
309
473
  }
310
474
  }
311
- updateProgress(progress) {
312
- this.updateState('serviceArea', { progress });
313
- this.logInfo(`progress updated: ${progress.length} areas`);
475
+ async handleServiceArea() {
476
+ this.logWarn(`Service area functionality is not supported for model: ${this.model}`);
477
+ // Placeholder for potential future support.
314
478
  }
315
- /**
316
- * Update battery percentage
317
- * @param percentage - Battery percentage (0-100)
318
- */
319
- async updateBatteryPercentage(percentage) {
320
- // Convert percentage to Matter format (0-200, where 200 = 100%)
321
- const batPercentRemaining = Math.max(0, Math.min(200, Math.round(percentage * 2)));
322
- // Determine charge level based on percentage
323
- let batChargeLevel = 0; // Ok
324
- if (percentage < 20) {
325
- batChargeLevel = 2; // Critical
479
+ async simulateCleaningSequence() {
480
+ this.logInfo('Starting cleaning sequence simulation.');
481
+ this.updateOperationalState(1); // Set to Running
482
+ setTimeout(async () => {
483
+ this.logInfo('Cleaning sequence timer expired. Requesting device status update.');
484
+ try {
485
+ const status = await this.requestDeviceStatus();
486
+ if (status && status.operationalState) {
487
+ this.logInfo(`Device status found: ${status.operationalState}`);
488
+ this.updateOperationalState(status.operationalState);
489
+ }
490
+ else {
491
+ this.logWarn('Device status not found. Setting operational state to Stopped.');
492
+ this.updateOperationalState(0); // Default to Stopped
493
+ }
494
+ }
495
+ catch (error) {
496
+ this.logWarn(`Failed to fetch device status: ${String(error?.message ?? error)}`);
497
+ this.updateOperationalState(0); // Default to Stopped
498
+ }
499
+ }, 5000); // Simulate a 5-second cleaning sequence
500
+ }
501
+ async simulateDockingSequence() {
502
+ this.logInfo('Starting docking sequence simulation.');
503
+ this.updateOperationalState(64); // Set to Seeking Charger
504
+ setTimeout(async () => {
505
+ this.logInfo('Docking sequence timer expired. Requesting device status update.');
506
+ try {
507
+ const status = await this.requestDeviceStatus();
508
+ if (status && status.operationalState) {
509
+ this.logInfo(`Device status found: ${status.operationalState}`);
510
+ this.updateOperationalState(status.operationalState);
511
+ }
512
+ else {
513
+ this.logWarn('Device status not found. Setting operational state to Docked.');
514
+ this.updateOperationalState(66); // Default to Docked
515
+ }
516
+ }
517
+ catch (error) {
518
+ this.logWarn(`Failed to fetch device status: ${String(error?.message ?? error)}`);
519
+ this.updateOperationalState(66); // Default to Docked
520
+ }
521
+ }, 5000); // Simulate a 5-second docking sequence
522
+ }
523
+ async requestDeviceStatus() {
524
+ this.logInfo('Requesting device status from OpenAPI.');
525
+ try {
526
+ const response = await this.sendOpenAPICommand('getDeviceStatus');
527
+ this.logInfo(`Device status response: ${JSON.stringify(response)}`);
528
+ return response;
326
529
  }
327
- else if (percentage < 40) {
328
- batChargeLevel = 1; // Warning
530
+ catch (error) {
531
+ this.logWarn(`Failed to request device status: ${String(error?.message ?? error)}`);
532
+ return null;
329
533
  }
330
- await this.updateState('powerSource', { batPercentRemaining, batChargeLevel });
331
- this.logInfo(`battery updated to: ${percentage}% (${batChargeLevel === 0 ? 'Ok' : batChargeLevel === 1 ? 'Warning' : 'Critical'})`);
534
+ }
535
+ async startCleaningSimulation() {
536
+ this.logInfo('Initiating cleaning simulation.');
537
+ await this.simulateCleaningSequence();
538
+ }
539
+ async startDockingSimulation() {
540
+ this.logInfo('Initiating docking simulation.');
541
+ await this.simulateDockingSequence();
332
542
  }
333
543
  }
334
544
  //# sourceMappingURL=RoboticVacuumAccessory.js.map