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

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.
@@ -270,13 +270,18 @@ export class RoboticVacuumAccessory extends BaseMatterAccessory {
270
270
  stop: async () => this.handleStop(),
271
271
  start: async () => this.handleStart(),
272
272
  goHome: async () => this.handleGoHome(),
273
+ // Always register resume handler, log warning if not supported
274
+ resume: async () => {
275
+ if (capabilities.resume) {
276
+ await this.handleResume()
277
+ } else {
278
+ this.logWarn(`Resume operation is not supported for model: ${this.model}`)
279
+ }
280
+ },
273
281
  }
274
282
  if (capabilities.pause) {
275
283
  opHandlers.pause = async () => this.handlePause()
276
284
  }
277
- if (capabilities.resume) {
278
- opHandlers.resume = async () => this.handleResume()
279
- }
280
285
  return {
281
286
  rvcRunMode: { changeToMode: async (request: MatterRequests.ChangeToMode) => this.handleChangeRunMode(request) },
282
287
  rvcCleanMode: { changeToMode: async (request: MatterRequests.ChangeToMode) => this.handleChangeCleanMode(request) },
@@ -456,7 +461,15 @@ export class RoboticVacuumAccessory extends BaseMatterAccessory {
456
461
 
457
462
  private async handleResume(): Promise<void> {
458
463
  this.logInfo('resume requested.')
459
- await this.handleStart()
464
+ try {
465
+ this.logInfo(`[OpenAPI] Sending resume command`)
466
+ await this.sendOpenAPICommand('resume')
467
+ this.logInfo(`[OpenAPI] resume command sent`)
468
+ } catch (e: any) {
469
+ this.logWarn(`OpenAPI resume failed: ${String(e?.message ?? e)}`)
470
+ }
471
+ this.updateRunMode(1)
472
+ this.updateOperationalState(1)
460
473
  }
461
474
 
462
475
  private async handleGoHome(): Promise<void> {
@@ -58,7 +58,7 @@ export class VenetianBlindAccessory extends BaseMatterAccessory {
58
58
  this.logInfo('initialized.')
59
59
  }
60
60
 
61
- private async handleGoToLift(request: MatterRequests.GoToLiftPercentage): Promise<void> {
61
+ public async handleGoToLift(request: MatterRequests.GoToLiftPercentage): Promise<void> {
62
62
  this.logInfo(`GoToLiftPercentage request: ${JSON.stringify(request)}`)
63
63
 
64
64
  // Matter uses 0=open, 10000=closed, so invert to get open percentage
@@ -68,7 +68,7 @@ export class VenetianBlindAccessory extends BaseMatterAccessory {
68
68
  // TODO: await myBlindAPI.setPosition(openPercent)
69
69
  }
70
70
 
71
- private async handleGoToTilt(request: MatterRequests.GoToTiltPercentage): Promise<void> {
71
+ public async handleGoToTilt(request: MatterRequests.GoToTiltPercentage): Promise<void> {
72
72
  this.logInfo(`GoToTiltPercentage request: ${JSON.stringify(request)}`)
73
73
 
74
74
  // Matter tilt: 0=horizontal/open (0deg), 10000=vertical/closed (90deg)
@@ -77,17 +77,17 @@ export class VenetianBlindAccessory extends BaseMatterAccessory {
77
77
  // TODO: await myBlindAPI.setTiltAngle(degrees)
78
78
  }
79
79
 
80
- private async handleUpOrOpen(): Promise<void> {
80
+ public async handleUpOrOpen(): Promise<void> {
81
81
  this.logInfo('opened blind.')
82
82
  // TODO: await myBlindAPI.open()
83
83
  }
84
84
 
85
- private async handleDownOrClose(): Promise<void> {
85
+ public async handleDownOrClose(): Promise<void> {
86
86
  this.logInfo('closed blind.')
87
87
  // TODO: await myBlindAPI.close()
88
88
  }
89
89
 
90
- private async handleStop(): Promise<void> {
90
+ public async handleStop(): Promise<void> {
91
91
  this.logInfo('stopped blind.')
92
92
  // TODO: await myBlindAPI.stop()
93
93
  }
@@ -55,28 +55,71 @@ export class WindowBlindAccessory extends BaseMatterAccessory {
55
55
  this.logInfo('initialized.')
56
56
  }
57
57
 
58
- private async handleGoToLift(request: MatterRequests.GoToLiftPercentage): Promise<void> {
58
+ public async handleGoToLift(request: MatterRequests.GoToLiftPercentage): Promise<void> {
59
59
  this.logInfo(`GoToLiftPercentage request: ${JSON.stringify(request)}`)
60
60
  // Matter uses 0=open, 10000=closed, so invert to get open percentage
61
61
  const closedPercent = request.liftPercent100thsValue / 100
62
- const openPercent = (100 - closedPercent).toFixed(0)
63
- this.logInfo(`moved to ${openPercent}% open.`)
64
- // TODO: await myBlindAPI.setPosition(openPercent)
62
+ // SwitchBot API expects position: 0=open, 100=closed
63
+ const position = Math.max(0, Math.min(100, closedPercent))
64
+ // Default to performance mode (ff), index is always 0
65
+ const mode = 'ff' // or '01' for silent mode if needed
66
+ const parameter = `0,${mode},${position}`
67
+ this.logInfo(`Sending setPosition to OpenAPI: parameter=${parameter}`)
68
+ try {
69
+ if (this.context?.sendOpenAPI) {
70
+ await this.sendOpenAPICommand('setPosition', parameter)
71
+ this.logInfo('OpenAPI setPosition command sent.')
72
+ } else {
73
+ this.logWarn('OpenAPI sender not available in context.')
74
+ }
75
+ } catch (e: any) {
76
+ this.logWarn(`OpenAPI setPosition failed: ${String(e?.message ?? e)}`)
77
+ }
65
78
  }
66
79
 
67
- private async handleUpOrOpen(): Promise<void> {
80
+ public async handleUpOrOpen(): Promise<void> {
68
81
  this.logInfo('opened blind.')
69
- // TODO: await myBlindAPI.open()
82
+ try {
83
+ // Send open command to SwitchBot OpenAPI
84
+ if (this.context?.sendOpenAPI) {
85
+ await this.sendOpenAPICommand('turnOn')
86
+ this.logInfo('OpenAPI open command sent.')
87
+ } else {
88
+ this.logWarn('OpenAPI sender not available in context.')
89
+ }
90
+ } catch (e: any) {
91
+ this.logWarn(`OpenAPI open failed: ${String(e?.message ?? e)}`)
92
+ }
70
93
  }
71
94
 
72
- private async handleDownOrClose(): Promise<void> {
95
+ public async handleDownOrClose(): Promise<void> {
73
96
  this.logInfo('closed blind.')
74
- // TODO: await myBlindAPI.close()
97
+ try {
98
+ // Send close command to SwitchBot OpenAPI
99
+ if (this.context?.sendOpenAPI) {
100
+ await this.sendOpenAPICommand('turnOff')
101
+ this.logInfo('OpenAPI close command sent.')
102
+ } else {
103
+ this.logWarn('OpenAPI sender not available in context.')
104
+ }
105
+ } catch (e: any) {
106
+ this.logWarn(`OpenAPI close failed: ${String(e?.message ?? e)}`)
107
+ }
75
108
  }
76
109
 
77
- private async handleStop(): Promise<void> {
110
+ public async handleStop(): Promise<void> {
78
111
  this.logInfo('stopped blind.')
79
- // TODO: await myBlindAPI.stop()
112
+ try {
113
+ // Send pause command to SwitchBot OpenAPI
114
+ if (this.context?.sendOpenAPI) {
115
+ await this.sendOpenAPICommand('pause')
116
+ this.logInfo('OpenAPI pause command sent.')
117
+ } else {
118
+ this.logWarn('OpenAPI sender not available in context.')
119
+ }
120
+ } catch (e: any) {
121
+ this.logWarn(`OpenAPI pause failed: ${String(e?.message ?? e)}`)
122
+ }
80
123
  }
81
124
 
82
125
  public updateLiftPosition(openPercent: number): void {
@@ -2344,13 +2344,31 @@ export class SwitchBotMatterPlatform implements DynamicPlatformPlugin {
2344
2344
 
2345
2345
  // Window Blind
2346
2346
  if (this.config.enableWindowBlind !== false) {
2347
- const device = new WindowBlindAccessory(this.api, this.log)
2347
+ const device = new WindowBlindAccessory(this.api, this.log, {
2348
+ handlers: {
2349
+ windowCovering: {
2350
+ goToLiftPercentage: async request => device?.handleGoToLift?.(request),
2351
+ upOrOpen: async () => device?.handleUpOrOpen?.(),
2352
+ downOrClose: async () => device?.handleDownOrClose?.(),
2353
+ stopMotion: async () => device?.handleStop?.(),
2354
+ },
2355
+ },
2356
+ })
2348
2357
  accessories.push(device.toAccessory())
2349
2358
  }
2350
2359
 
2351
2360
  // Venetian Blind
2352
2361
  if (this.config.enableVenetianBlind !== false) {
2353
- const device = new VenetianBlindAccessory(this.api, this.log)
2362
+ const device = new VenetianBlindAccessory(this.api, this.log, {
2363
+ handlers: {
2364
+ windowCovering: {
2365
+ goToLiftPercentage: async request => device?.handleGoToLift?.(request),
2366
+ upOrOpen: async () => device?.handleUpOrOpen?.(),
2367
+ downOrClose: async () => device?.handleDownOrClose?.(),
2368
+ stopMotion: async () => device?.handleStop?.(),
2369
+ },
2370
+ },
2371
+ })
2354
2372
  accessories.push(device.toAccessory())
2355
2373
  }
2356
2374
 
package/src/settings.ts CHANGED
@@ -88,11 +88,27 @@ export interface options {
88
88
  * When false (default), reset at UTC midnight. Default: false.
89
89
  */
90
90
  dailyApiResetAtLocalMidnight?: boolean
91
- // Matter platform batch refresh options
91
+ /**
92
+ * When true, resets the daily API request counter to zero. This is useful for testing purposes.
93
+ */
94
+ resetDailyApiCounter?: boolean
95
+ /**
96
+ * When set, configures the batch refresh rate (in milliseconds) for Matter devices. Default: 5000 ms.
97
+ */
92
98
  matterBatchRefreshRate?: number
99
+ /**
100
+ * When true, enables batch processing for Matter devices. Default: false.
101
+ */
93
102
  matterBatchConcurrency?: number
103
+ /**
104
+ * When true, enables batch processing for Matter devices. Default: false.
105
+ */
94
106
  matterBatchEnabled?: boolean
107
+ /**
108
+ * When set, adds a random delay (jitter) to batch requests to avoid thundering herd problems.
109
+ */
95
110
  matterBatchJitter?: number
111
+ newFeatureEnabled?: boolean
96
112
  };
97
113
 
98
114
  export type devicesConfig = botConfig | relaySwitch1Config | relaySwitch1PMConfig | meterConfig | meterProConfig | indoorOutdoorSensorConfig | humidifierConfig | curtainConfig | blindTiltConfig | contactConfig | motionConfig | waterDetectorConfig | plugConfig | colorBulbConfig | stripLightConfig | ceilingLightConfig | lockConfig | hubConfig