@switchbot/homebridge-switchbot 5.0.0-beta.63 → 5.0.0-beta.64

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.
@@ -180,32 +180,74 @@ export class RoboticVacuumAccessory extends BaseMatterAccessory {
180
180
  currentMode: 0,
181
181
  },
182
182
  rvcCleanMode: (() => {
183
- if (capabilities.cleanAction === 'vacuum-only') {
184
- return {
185
- supportedModes: [
186
- { label: 'Quiet', mode: 0, modeTags: [{ value: 2 }] },
187
- { label: 'Standard', mode: 1, modeTags: [{ value: 16384 }] },
188
- { label: 'Strong', mode: 2, modeTags: [{ value: 7 }] },
189
- { label: 'MAX', mode: 3, modeTags: [{ value: 8 }] },
190
- ],
191
- currentMode: 1,
192
- }
193
- } else if (capabilities.cleanAction === 'vacuum-or-mop') {
194
- return {
195
- supportedModes: [
196
- { label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
197
- { label: 'Mop', mode: 1, modeTags: [{ value: 16386 }] },
198
- ],
199
- currentMode: 0,
200
- }
201
- } else {
202
- return {
203
- supportedModes: [
204
- { label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
205
- { label: 'Vacuum & Mop', mode: 1, modeTags: [{ value: 16385 }, { value: 16386 }] },
206
- ],
207
- currentMode: 0,
208
- }
183
+ switch (model as string) {
184
+ case 'K10':
185
+ return {
186
+ supportedModes: [
187
+ { label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
188
+ ],
189
+ currentMode: 0,
190
+ }
191
+ case 'Robot Vacuum Cleaner S1':
192
+ case 'Robot Vacuum Cleaner S1 Plus':
193
+ return {
194
+ supportedModes: [
195
+ { label: 'Quiet', mode: 0, modeTags: [{ value: 2 }] },
196
+ { label: 'Standard', mode: 1, modeTags: [{ value: 16384 }] },
197
+ { label: 'Strong', mode: 2, modeTags: [{ value: 7 }] },
198
+ { label: 'MAX', mode: 3, modeTags: [{ value: 8 }] },
199
+ ],
200
+ currentMode: 1,
201
+ }
202
+ case 'Mini Robot Vacuum K10+':
203
+ case 'Mini Robot Vacuum K10+ Pro':
204
+ case 'K10+ Pro Combo':
205
+ return {
206
+ supportedModes: [
207
+ { label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
208
+ { label: 'Mop', mode: 1, modeTags: [{ value: 16386 }] },
209
+ { label: 'Sweep & Mop', mode: 2, modeTags: [{ value: 16385 }, { value: 16386 }] },
210
+ ],
211
+ currentMode: 0,
212
+ }
213
+ case 'Multitasking Household Robot K20+ Pro':
214
+ return {
215
+ supportedModes: [
216
+ { label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
217
+ { label: 'Vacuum & Mop', mode: 1, modeTags: [{ value: 16385 }, { value: 16386 }] },
218
+ { label: 'Deep Clean', mode: 2, modeTags: [{ value: 16384 }] },
219
+ ],
220
+ currentMode: 0,
221
+ }
222
+ case 'Floor Cleaning Robot S10':
223
+ case 'Floor Cleaning Robot S20':
224
+ return {
225
+ supportedModes: [
226
+ { label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
227
+ { label: 'Mop', mode: 1, modeTags: [{ value: 16386 }] },
228
+ ],
229
+ currentMode: 0,
230
+ }
231
+ case 'Robot Vacuum K11+':
232
+ return {
233
+ supportedModes: [
234
+ { label: 'Quiet', mode: 0, modeTags: [{ value: 2 }] },
235
+ { label: 'Standard', mode: 1, modeTags: [{ value: 16384 }] },
236
+ { label: 'Strong', mode: 2, modeTags: [{ value: 7 }] },
237
+ { label: 'MAX', mode: 3, modeTags: [{ value: 8 }] },
238
+ { label: 'Deep Clean', mode: 4, modeTags: [{ value: 16384 }, { value: 16385 }] },
239
+ ],
240
+ currentMode: 1,
241
+ }
242
+ default:
243
+ return {
244
+ supportedModes: [
245
+ { label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
246
+ { label: 'Vacuum & Mop', mode: 1, modeTags: [{ value: 16385 }, { value: 16386 }] },
247
+ { label: 'Deep Clean', mode: 2, modeTags: [{ value: 16384 }] },
248
+ ],
249
+ currentMode: 0,
250
+ }
209
251
  }
210
252
  })(),
211
253
  rvcOperationalState: {
@@ -213,9 +255,12 @@ export class RoboticVacuumAccessory extends BaseMatterAccessory {
213
255
  { operationalStateId: 0 }, // Stopped
214
256
  { operationalStateId: 1 }, // Running
215
257
  { operationalStateId: 2 }, // Paused
258
+ { operationalStateId: 3 }, // Error
216
259
  { operationalStateId: 64 }, // Seeking Charger
217
260
  { operationalStateId: 65 }, // Charging
218
261
  { operationalStateId: 66 }, // Docked
262
+ { operationalStateId: 67 }, // In Remote Control
263
+ { operationalStateId: 68 }, // In Dust Collecting
219
264
  ],
220
265
  operationalState: 66,
221
266
  },
@@ -248,58 +293,110 @@ export class RoboticVacuumAccessory extends BaseMatterAccessory {
248
293
  private async handleChangeRunMode(request: MatterRequests.ChangeToMode): Promise<void> {
249
294
  this.logInfo(`ChangeToMode (run) request received: ${JSON.stringify(request)}`)
250
295
  const { newMode } = request
251
- const modeStr = ['Idle', 'Cleaning'][newMode] || `Unknown (mode=${newMode})`
252
- this.logInfo(`changing run mode to: ${modeStr}`)
253
- if (newMode === 1) {
254
- await this.handleStart()
255
- } else if (newMode === 0) {
256
- await this.handleGoHome()
296
+ const modeStr = ['Idle', 'Cleaning', 'Returning to Dock'][newMode] || `Unknown (mode=${newMode})`
297
+ this.logInfo(`Changing run mode to: ${modeStr}`)
298
+
299
+ switch (this.model) {
300
+ case 'Robot Vacuum Cleaner S1':
301
+ case 'Robot Vacuum Cleaner S1 Plus': {
302
+ if (newMode === 1) {
303
+ await this.handleStart()
304
+ } else if (newMode === 0) {
305
+ await this.handleGoHome()
306
+ }
307
+ break
308
+ }
309
+ case 'Mini Robot Vacuum K10+':
310
+ case 'Mini Robot Vacuum K10+ Pro':
311
+ case 'K10+ Pro Combo': {
312
+ if (newMode === 1) {
313
+ await this.handleStart()
314
+ } else if (newMode === 2) {
315
+ await this.handleDock()
316
+ }
317
+ break
318
+ }
319
+ case 'Multitasking Household Robot K20+ Pro': {
320
+ if (newMode === 1) {
321
+ await this.handleStart()
322
+ } else if (newMode === 0) {
323
+ await this.handleGoHome()
324
+ } else if (newMode === 2) {
325
+ await this.handlePause()
326
+ }
327
+ break
328
+ }
329
+ case 'Floor Cleaning Robot S10':
330
+ case 'Floor Cleaning Robot S20': {
331
+ if (newMode === 1) {
332
+ await this.handleStart()
333
+ } else if (newMode === 0) {
334
+ await this.handleGoHome()
335
+ }
336
+ break
337
+ }
338
+ case 'Robot Vacuum K11+': {
339
+ if (newMode === 1) {
340
+ await this.handleStart()
341
+ } else if (newMode === 0) {
342
+ await this.handleGoHome()
343
+ } else if (newMode === 2) {
344
+ await this.handleDock()
345
+ }
346
+ break
347
+ }
348
+ default:
349
+ this.logWarn(`Run mode change not supported for model: ${this.model}`)
257
350
  }
258
351
  }
259
352
 
260
353
  private async handleChangeCleanMode(request: MatterRequests.ChangeToMode): Promise<void> {
261
354
  this.logInfo(`ChangeToMode (clean) request received: ${JSON.stringify(request)}`)
262
355
  const { newMode } = request
263
- if (this.capabilities.cleanAction === 'vacuum-only') {
264
- const mapPow = ['Quiet', 'Standard', 'Strong', 'MAX'] as const
265
- const label = mapPow[newMode] ?? `Unknown (${newMode})`
266
- this.logInfo(`changing suction level to: ${label}`)
267
- if (this.capabilities.suctionKind === 'powLevel-0-3') {
268
- try {
269
- this.logInfo(`[OpenAPI] Sending PowLevel: ${newMode}`)
270
- await this.sendOpenAPICommand('PowLevel', String(newMode))
271
- this.logInfo(`[OpenAPI] PowLevel command sent: ${newMode}`)
272
- } catch (e: any) {
273
- this.logWarn(`OpenAPI PowLevel failed: ${String(e?.message ?? e)}`)
274
- }
275
- } else if (this.capabilities.suctionKind === 'fanLevel-1-4') {
276
- this.currentFanLevel = (Math.min(3, Math.max(0, Number(newMode))) + 1) as 1 | 2 | 3 | 4
277
- try {
278
- this.logInfo(`[OpenAPI] Sending changeParam fanLevel: ${this.currentFanLevel}`)
279
- await this.sendOpenAPICommand('changeParam', JSON.stringify({ fanLevel: this.currentFanLevel }))
280
- this.logInfo(`[OpenAPI] changeParam command sent: fanLevel=${this.currentFanLevel}`)
281
- } catch (e: any) {
282
- this.logWarn(`OpenAPI changeParam(fanLevel) failed: ${String(e?.message ?? e)}`)
283
- }
284
- }
285
- this.updateCleanMode(newMode)
286
- return
287
- }
288
356
 
289
- if (this.capabilities.cleanAction === 'vacuum-or-mop') {
290
- const label = newMode === 0 ? 'Vacuum' : 'Mop'
291
- this.currentCleanAction = newMode === 0 ? 'vacuum' : 'mop'
292
- this.logInfo(`changing clean action to: ${label}`)
293
- this.logInfo(`[OpenAPI] (no command sent for cleanAction change, just updating state)`)
294
- this.updateCleanMode(newMode)
295
- return
357
+ switch (this.model) {
358
+ case 'Robot Vacuum Cleaner S1':
359
+ case 'Robot Vacuum Cleaner S1 Plus': {
360
+ const mapPowS1 = ['Quiet', 'Standard', 'Strong', 'MAX'] as const
361
+ const labelS1 = mapPowS1[newMode] ?? `Unknown (${newMode})`
362
+ this.logInfo(`Changing suction level to: ${labelS1}`)
363
+ await this.sendOpenAPICommand('PowLevel', String(newMode))
364
+ break
365
+ }
366
+ case 'Mini Robot Vacuum K10+':
367
+ case 'Mini Robot Vacuum K10+ Pro':
368
+ case 'K10+ Pro Combo': {
369
+ const mapPowK10 = ['Vacuum', 'Mop', 'Sweep & Mop'] as const
370
+ const labelK10 = mapPowK10[newMode] ?? `Unknown (${newMode})`
371
+ this.logInfo(`Changing cleaning mode to: ${labelK10}`)
372
+ await this.sendOpenAPICommand('CleanMode', String(newMode))
373
+ break
374
+ }
375
+ case 'Multitasking Household Robot K20+ Pro': {
376
+ const mapPowK20 = ['Vacuum', 'Vacuum & Mop', 'Deep Clean'] as const
377
+ const labelK20 = mapPowK20[newMode] ?? `Unknown (${newMode})`
378
+ this.logInfo(`Changing cleaning mode to: ${labelK20}`)
379
+ await this.sendOpenAPICommand('CleanMode', String(newMode))
380
+ break
381
+ }
382
+ case 'Floor Cleaning Robot S10':
383
+ case 'Floor Cleaning Robot S20': {
384
+ const mapPowS10 = ['Vacuum', 'Mop'] as const
385
+ const labelS10 = mapPowS10[newMode] ?? `Unknown (${newMode})`
386
+ this.logInfo(`Changing cleaning mode to: ${labelS10}`)
387
+ await this.sendOpenAPICommand('CleanMode', String(newMode))
388
+ break
389
+ }
390
+ case 'Robot Vacuum K11+': {
391
+ const mapPowK11 = ['Quiet', 'Standard', 'Strong', 'MAX', 'Deep Clean'] as const
392
+ const labelK11 = mapPowK11[newMode] ?? `Unknown (${newMode})`
393
+ this.logInfo(`Changing suction level to: ${labelK11}`)
394
+ await this.sendOpenAPICommand('PowLevel', String(newMode))
395
+ break
396
+ }
397
+ default:
398
+ this.logWarn(`Clean mode change not supported for model: ${this.model}`)
296
399
  }
297
-
298
- const label = newMode === 0 ? 'Vacuum' : 'Vacuum & Mop'
299
- this.currentCleanAction = newMode === 0 ? 'vacuum' : 'vacuum_mop'
300
- this.logInfo(`changing clean action to: ${label}`)
301
- this.logInfo(`[OpenAPI] (no command sent for cleanAction change, just updating state)`)
302
- this.updateCleanMode(newMode)
303
400
  }
304
401
 
305
402
  private async handlePause(): Promise<void> {
@@ -397,14 +494,7 @@ export class RoboticVacuumAccessory extends BaseMatterAccessory {
397
494
 
398
495
  public updateCleanMode(mode: number): void {
399
496
  this.updateState('rvcCleanMode', { currentMode: mode })
400
- let label = `Unknown (${mode})`
401
- if (this.capabilities.cleanAction === 'vacuum-only') {
402
- label = ['Quiet', 'Standard', 'Strong', 'MAX'][mode] || label
403
- } else if (this.capabilities.cleanAction === 'vacuum-or-mop') {
404
- label = ['Vacuum', 'Mop'][mode] || label
405
- } else {
406
- label = ['Vacuum', 'Vacuum & Mop'][mode] || label
407
- }
497
+ const label = mode === 0 ? 'Vacuum' : mode === 1 ? 'Mop' : mode === 2 ? 'Vacuum & Mop' : 'Unknown'
408
498
  this.logInfo(`clean mode updated to: ${label}`)
409
499
  }
410
500
 
@@ -431,4 +521,88 @@ export class RoboticVacuumAccessory extends BaseMatterAccessory {
431
521
  ;(this.context as any).batteryChargeLevel = chargeLevel
432
522
  }
433
523
  }
524
+
525
+ private async handleDock(): Promise<void> {
526
+ this.logInfo('Docking the vacuum.')
527
+ try {
528
+ this.logInfo('[OpenAPI] Sending dock command')
529
+ await this.sendOpenAPICommand('dock')
530
+ this.logInfo('[OpenAPI] Dock command sent successfully')
531
+ this.updateRunMode(0) // Set to Idle after docking
532
+ this.updateOperationalState(64) // Seeking Charger state
533
+ } catch (error: any) {
534
+ this.logWarn(`Docking failed: ${String(error?.message ?? error)}`)
535
+ }
536
+ }
537
+
538
+ private async handleServiceArea(): Promise<void> {
539
+ this.logWarn(`Service area functionality is not supported for model: ${this.model}`)
540
+ // Placeholder for potential future support.
541
+ }
542
+
543
+ private async simulateCleaningSequence(): Promise<void> {
544
+ this.logInfo('Starting cleaning sequence simulation.')
545
+ this.updateOperationalState(1) // Set to Running
546
+
547
+ setTimeout(async () => {
548
+ this.logInfo('Cleaning sequence timer expired. Requesting device status update.')
549
+ try {
550
+ const status = await this.requestDeviceStatus()
551
+ if (status && status.operationalState) {
552
+ this.logInfo(`Device status found: ${status.operationalState}`)
553
+ this.updateOperationalState(status.operationalState)
554
+ } else {
555
+ this.logWarn('Device status not found. Setting operational state to Stopped.')
556
+ this.updateOperationalState(0) // Default to Stopped
557
+ }
558
+ } catch (error: any) {
559
+ this.logWarn(`Failed to fetch device status: ${String(error?.message ?? error)}`)
560
+ this.updateOperationalState(0) // Default to Stopped
561
+ }
562
+ }, 5000) // Simulate a 5-second cleaning sequence
563
+ }
564
+
565
+ private async simulateDockingSequence(): Promise<void> {
566
+ this.logInfo('Starting docking sequence simulation.')
567
+ this.updateOperationalState(64) // Set to Seeking Charger
568
+
569
+ setTimeout(async () => {
570
+ this.logInfo('Docking sequence timer expired. Requesting device status update.')
571
+ try {
572
+ const status = await this.requestDeviceStatus()
573
+ if (status && status.operationalState) {
574
+ this.logInfo(`Device status found: ${status.operationalState}`)
575
+ this.updateOperationalState(status.operationalState)
576
+ } else {
577
+ this.logWarn('Device status not found. Setting operational state to Docked.')
578
+ this.updateOperationalState(66) // Default to Docked
579
+ }
580
+ } catch (error: any) {
581
+ this.logWarn(`Failed to fetch device status: ${String(error?.message ?? error)}`)
582
+ this.updateOperationalState(66) // Default to Docked
583
+ }
584
+ }, 5000) // Simulate a 5-second docking sequence
585
+ }
586
+
587
+ private async requestDeviceStatus(): Promise<{ operationalState?: number } | null> {
588
+ this.logInfo('Requesting device status from OpenAPI.')
589
+ try {
590
+ const response = await this.sendOpenAPICommand('getDeviceStatus')
591
+ this.logInfo(`Device status response: ${JSON.stringify(response)}`)
592
+ return response
593
+ } catch (error: any) {
594
+ this.logWarn(`Failed to request device status: ${String(error?.message ?? error)}`)
595
+ return null
596
+ }
597
+ }
598
+
599
+ public async startCleaningSimulation(): Promise<void> {
600
+ this.logInfo('Initiating cleaning simulation.')
601
+ await this.simulateCleaningSequence()
602
+ }
603
+
604
+ public async startDockingSimulation(): Promise<void> {
605
+ this.logInfo('Initiating docking simulation.')
606
+ await this.simulateDockingSequence()
607
+ }
434
608
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=roboticVacuumAccessory.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"roboticVacuumAccessory.test.d.ts","sourceRoot":"","sources":["../../../../src/test/matter/devices-matter/roboticVacuumAccessory.test.ts"],"names":[],"mappings":""}