@switchbot/homebridge-switchbot 5.0.0-beta.42 → 5.0.0-beta.44
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.
- package/CHANGELOG.md +2 -0
- package/README.md +22 -0
- package/config.schema.json +71 -9
- package/dist/devices-hap/airpurifier.d.ts.map +1 -1
- package/dist/devices-hap/airpurifier.js +12 -6
- package/dist/devices-hap/airpurifier.js.map +1 -1
- package/dist/devices-hap/blindtilt.js +3 -3
- package/dist/devices-hap/bot.d.ts.map +1 -1
- package/dist/devices-hap/bot.js +16 -5
- package/dist/devices-hap/bot.js.map +1 -1
- package/dist/devices-hap/ceilinglight.d.ts.map +1 -1
- package/dist/devices-hap/ceilinglight.js +13 -7
- package/dist/devices-hap/ceilinglight.js.map +1 -1
- package/dist/devices-hap/colorbulb.d.ts.map +1 -1
- package/dist/devices-hap/colorbulb.js +49 -9
- package/dist/devices-hap/colorbulb.js.map +1 -1
- package/dist/devices-hap/contact.js +3 -3
- package/dist/devices-hap/curtain.js +2 -2
- package/dist/devices-hap/curtain.js.map +1 -1
- package/dist/devices-hap/device.d.ts.map +1 -1
- package/dist/devices-hap/device.js +20 -1
- package/dist/devices-hap/device.js.map +1 -1
- package/dist/devices-hap/fan.d.ts.map +1 -1
- package/dist/devices-hap/fan.js +12 -6
- package/dist/devices-hap/fan.js.map +1 -1
- package/dist/devices-hap/hub.d.ts.map +1 -1
- package/dist/devices-hap/hub.js +6 -5
- package/dist/devices-hap/hub.js.map +1 -1
- package/dist/devices-hap/humidifier.d.ts +5 -0
- package/dist/devices-hap/humidifier.d.ts.map +1 -1
- package/dist/devices-hap/humidifier.js +92 -4
- package/dist/devices-hap/humidifier.js.map +1 -1
- package/dist/devices-hap/iosensor.d.ts.map +1 -1
- package/dist/devices-hap/iosensor.js +36 -21
- package/dist/devices-hap/iosensor.js.map +1 -1
- package/dist/devices-hap/lightstrip.d.ts.map +1 -1
- package/dist/devices-hap/lightstrip.js +38 -8
- package/dist/devices-hap/lightstrip.js.map +1 -1
- package/dist/devices-hap/lock.d.ts.map +1 -1
- package/dist/devices-hap/lock.js +14 -6
- package/dist/devices-hap/lock.js.map +1 -1
- package/dist/devices-hap/meter.d.ts.map +1 -1
- package/dist/devices-hap/meter.js +6 -5
- package/dist/devices-hap/meter.js.map +1 -1
- package/dist/devices-hap/meterplus.d.ts.map +1 -1
- package/dist/devices-hap/meterplus.js +6 -5
- package/dist/devices-hap/meterplus.js.map +1 -1
- package/dist/devices-hap/meterpro.d.ts.map +1 -1
- package/dist/devices-hap/meterpro.js +7 -6
- package/dist/devices-hap/meterpro.js.map +1 -1
- package/dist/devices-hap/motion.js +3 -3
- package/dist/devices-hap/plug.d.ts.map +1 -1
- package/dist/devices-hap/plug.js +11 -6
- package/dist/devices-hap/plug.js.map +1 -1
- package/dist/devices-hap/relayswitch.js +3 -3
- package/dist/devices-hap/robotvacuumcleaner.d.ts.map +1 -1
- package/dist/devices-hap/robotvacuumcleaner.js +13 -6
- package/dist/devices-hap/robotvacuumcleaner.js.map +1 -1
- package/dist/devices-hap/waterdetector.js +3 -3
- package/dist/homebridge-ui/public/index.html +13 -1
- package/dist/platform-hap.d.ts +5 -0
- package/dist/platform-hap.d.ts.map +1 -1
- package/dist/platform-hap.js +106 -16
- package/dist/platform-hap.js.map +1 -1
- package/dist/platform-matter.d.ts +5 -0
- package/dist/platform-matter.d.ts.map +1 -1
- package/dist/platform-matter.js +129 -8
- package/dist/platform-matter.js.map +1 -1
- package/dist/settings.d.ts +17 -1
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js.map +1 -1
- package/dist/test/hap/device-webhook-context.test.d.ts +2 -0
- package/dist/test/hap/device-webhook-context.test.d.ts.map +1 -0
- package/dist/test/hap/device-webhook-context.test.js +128 -0
- package/dist/test/hap/device-webhook-context.test.js.map +1 -0
- package/dist/test/matter/platform-matter.webhook.test.d.ts +2 -0
- package/dist/test/matter/platform-matter.webhook.test.d.ts.map +1 -0
- package/dist/test/matter/platform-matter.webhook.test.js +46 -0
- package/dist/test/matter/platform-matter.webhook.test.js.map +1 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +74 -20
- package/dist/utils.js.map +1 -1
- package/docs/assets/highlight.css +14 -0
- package/docs/index.html +12 -1
- package/docs/variables/default.html +1 -1
- package/package.json +2 -2
- package/src/devices-hap/airpurifier.ts +11 -6
- package/src/devices-hap/blindtilt.ts +3 -3
- package/src/devices-hap/bot.ts +15 -5
- package/src/devices-hap/ceilinglight.ts +12 -7
- package/src/devices-hap/colorbulb.ts +46 -10
- package/src/devices-hap/contact.ts +3 -3
- package/src/devices-hap/curtain.ts +2 -2
- package/src/devices-hap/device.ts +20 -1
- package/src/devices-hap/fan.ts +11 -6
- package/src/devices-hap/hub.ts +6 -5
- package/src/devices-hap/humidifier.ts +97 -4
- package/src/devices-hap/iosensor.ts +36 -21
- package/src/devices-hap/lightstrip.ts +35 -8
- package/src/devices-hap/lock.ts +13 -6
- package/src/devices-hap/meter.ts +6 -5
- package/src/devices-hap/meterplus.ts +6 -5
- package/src/devices-hap/meterpro.ts +7 -6
- package/src/devices-hap/motion.ts +3 -3
- package/src/devices-hap/plug.ts +10 -6
- package/src/devices-hap/relayswitch.ts +3 -3
- package/src/devices-hap/robotvacuumcleaner.ts +12 -6
- package/src/devices-hap/waterdetector.ts +3 -3
- package/src/homebridge-ui/public/index.html +13 -1
- package/src/platform-hap.ts +110 -16
- package/src/platform-matter.ts +137 -8
- package/src/settings.ts +17 -1
- package/src/test/hap/device-webhook-context.test.ts +136 -0
- package/src/test/matter/platform-matter.webhook.test.ts +54 -0
- package/src/utils.ts +88 -29
package/src/devices-hap/lock.ts
CHANGED
|
@@ -332,7 +332,7 @@ export class Lock extends deviceBase {
|
|
|
332
332
|
if ((serviceData.model === SwitchBotBLEModel.Lock || SwitchBotBLEModel.LockPro)
|
|
333
333
|
&& (serviceData.modelName === SwitchBotBLEModelName.Lock || SwitchBotBLEModelName.LockPro)) {
|
|
334
334
|
this.serviceData = serviceData
|
|
335
|
-
if (serviceData !== undefined
|
|
335
|
+
if (serviceData !== undefined && serviceData !== null) {
|
|
336
336
|
await this.BLEparseStatus()
|
|
337
337
|
await this.updateHomeKitCharacteristics()
|
|
338
338
|
} else {
|
|
@@ -358,7 +358,7 @@ export class Lock extends deviceBase {
|
|
|
358
358
|
this.platform.bleEventHandler[this.device.bleMac] = async (context: lockServiceData | lockProServiceData) => {
|
|
359
359
|
try {
|
|
360
360
|
this.serviceData = context
|
|
361
|
-
if (context !== undefined
|
|
361
|
+
if (context !== undefined && context !== null) {
|
|
362
362
|
this.debugLog(`received BLE: ${JSON.stringify(context)}`)
|
|
363
363
|
await this.BLEparseStatus()
|
|
364
364
|
await this.updateHomeKitCharacteristics()
|
|
@@ -403,7 +403,7 @@ export class Lock extends deviceBase {
|
|
|
403
403
|
this.platform.webhookEventHandler[this.device.deviceId] = async (context: lockWebhookContext | lockProWebhookContext) => {
|
|
404
404
|
try {
|
|
405
405
|
this.webhookContext = context
|
|
406
|
-
if (context !== undefined
|
|
406
|
+
if (context !== undefined && context !== null) {
|
|
407
407
|
this.debugLog(`received Webhook: ${JSON.stringify(context)}`)
|
|
408
408
|
await this.parseStatusWebhook()
|
|
409
409
|
await this.updateHomeKitCharacteristics()
|
|
@@ -455,13 +455,20 @@ export class Lock extends deviceBase {
|
|
|
455
455
|
switchBotBLE
|
|
456
456
|
.discover({ model: this.device.bleModel, id: this.device.bleMac })
|
|
457
457
|
.then(async (device_list: SwitchbotDevice[]) => {
|
|
458
|
+
const deviceList = device_list as WoSmartLock[]
|
|
459
|
+
this.infoLog(`LockTargetState: ${this.LockMechanism.LockTargetState}`)
|
|
460
|
+
this.warnLog(`device_list: ${JSON.stringify(device_list)}`)
|
|
458
461
|
return await this.retryBLE({
|
|
459
462
|
max: this.maxRetryBLE(),
|
|
460
463
|
fn: async () => {
|
|
461
|
-
if (
|
|
462
|
-
|
|
464
|
+
if (deviceList && Array.isArray(deviceList) && deviceList.length > 0) {
|
|
465
|
+
if (this.LockMechanism.LockTargetState === this.hap.Characteristic.LockTargetState.SECURED) {
|
|
466
|
+
return await deviceList[0].lock()
|
|
467
|
+
} else {
|
|
468
|
+
return await deviceList[0].unlock()
|
|
469
|
+
}
|
|
463
470
|
} else {
|
|
464
|
-
|
|
471
|
+
throw new Error('No devices found during discovery.')
|
|
465
472
|
}
|
|
466
473
|
},
|
|
467
474
|
})
|
package/src/devices-hap/meter.ts
CHANGED
|
@@ -209,8 +209,9 @@ export class Meter extends deviceBase {
|
|
|
209
209
|
|
|
210
210
|
// CurrentTemperature
|
|
211
211
|
if (!(this.device as meterConfig).hide_temperature && this.TemperatureSensor?.Service) {
|
|
212
|
-
|
|
213
|
-
this.
|
|
212
|
+
// OpenAPI returns Celsius; convert if user configured a different unit
|
|
213
|
+
this.TemperatureSensor.CurrentTemperature = convertUnits(this.deviceStatus.temperature, 'CELSIUS', (this.device as meterConfig).convertUnitTo)
|
|
214
|
+
this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}`)
|
|
214
215
|
}
|
|
215
216
|
|
|
216
217
|
// BatteryLevel
|
|
@@ -287,7 +288,7 @@ export class Meter extends deviceBase {
|
|
|
287
288
|
// Update HomeKit
|
|
288
289
|
if (serviceData.model === SwitchBotBLEModel.Meter && serviceData.modelName === SwitchBotBLEModelName.Meter) {
|
|
289
290
|
this.serviceData = serviceData
|
|
290
|
-
if (serviceData !== undefined
|
|
291
|
+
if (serviceData !== undefined && serviceData !== null) {
|
|
291
292
|
await this.BLEparseStatus()
|
|
292
293
|
await this.updateHomeKitCharacteristics()
|
|
293
294
|
} else {
|
|
@@ -313,7 +314,7 @@ export class Meter extends deviceBase {
|
|
|
313
314
|
this.platform.bleEventHandler[this.device.bleMac] = async (context: meterServiceData) => {
|
|
314
315
|
try {
|
|
315
316
|
this.serviceData = context
|
|
316
|
-
if (context !== undefined
|
|
317
|
+
if (context !== undefined && context !== null) {
|
|
317
318
|
this.debugLog(`received BLE: ${JSON.stringify(context)}`)
|
|
318
319
|
await this.BLEparseStatus()
|
|
319
320
|
await this.updateHomeKitCharacteristics()
|
|
@@ -358,7 +359,7 @@ export class Meter extends deviceBase {
|
|
|
358
359
|
this.platform.webhookEventHandler[this.device.deviceId] = async (context: meterWebhookContext) => {
|
|
359
360
|
try {
|
|
360
361
|
this.webhookContext = context
|
|
361
|
-
if (context !== undefined
|
|
362
|
+
if (context !== undefined && context !== null) {
|
|
362
363
|
this.debugLog(`received Webhook: ${JSON.stringify(context)}`)
|
|
363
364
|
await this.parseStatusWebhook()
|
|
364
365
|
await this.updateHomeKitCharacteristics()
|
|
@@ -213,8 +213,9 @@ export class MeterPlus extends deviceBase {
|
|
|
213
213
|
|
|
214
214
|
// CurrentTemperature
|
|
215
215
|
if (!(this.device as meterConfig).hide_temperature && this.TemperatureSensor?.Service) {
|
|
216
|
-
|
|
217
|
-
this.
|
|
216
|
+
// OpenAPI returns Celsius; convert if user configured a different unit
|
|
217
|
+
this.TemperatureSensor.CurrentTemperature = convertUnits(this.deviceStatus.temperature, 'CELSIUS', (this.device as meterConfig).convertUnitTo)
|
|
218
|
+
this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}`)
|
|
218
219
|
}
|
|
219
220
|
|
|
220
221
|
// BatteryLevel
|
|
@@ -291,7 +292,7 @@ export class MeterPlus extends deviceBase {
|
|
|
291
292
|
// Update HomeKit
|
|
292
293
|
if (serviceData.model === SwitchBotBLEModel.MeterPlus && serviceData.modelName === SwitchBotBLEModelName.MeterPlus) {
|
|
293
294
|
this.serviceData = serviceData
|
|
294
|
-
if (serviceData !== undefined
|
|
295
|
+
if (serviceData !== undefined && serviceData !== null) {
|
|
295
296
|
await this.BLEparseStatus()
|
|
296
297
|
await this.updateHomeKitCharacteristics()
|
|
297
298
|
} else {
|
|
@@ -317,7 +318,7 @@ export class MeterPlus extends deviceBase {
|
|
|
317
318
|
this.platform.bleEventHandler[this.device.bleMac] = async (context: meterPlusServiceData) => {
|
|
318
319
|
try {
|
|
319
320
|
this.serviceData = context
|
|
320
|
-
if (context !== undefined
|
|
321
|
+
if (context !== undefined && context !== null) {
|
|
321
322
|
this.debugLog(`received BLE: ${JSON.stringify(context)}`)
|
|
322
323
|
await this.BLEparseStatus()
|
|
323
324
|
await this.updateHomeKitCharacteristics()
|
|
@@ -362,7 +363,7 @@ export class MeterPlus extends deviceBase {
|
|
|
362
363
|
this.platform.webhookEventHandler[this.device.deviceId] = async (context: meterPlusWebhookContext) => {
|
|
363
364
|
try {
|
|
364
365
|
this.webhookContext = context
|
|
365
|
-
if (context !== undefined
|
|
366
|
+
if (context !== undefined && context !== null) {
|
|
366
367
|
this.debugLog(`received Webhook: ${JSON.stringify(context)}`)
|
|
367
368
|
await this.parseStatusWebhook()
|
|
368
369
|
await this.updateHomeKitCharacteristics()
|
|
@@ -263,8 +263,9 @@ export class MeterPro extends deviceBase {
|
|
|
263
263
|
|
|
264
264
|
// CurrentTemperature
|
|
265
265
|
if (!(this.device as meterProConfig).hide_temperature && this.TemperatureSensor?.Service) {
|
|
266
|
-
|
|
267
|
-
this.
|
|
266
|
+
// OpenAPI returns Celsius; convert if user configured a different unit
|
|
267
|
+
this.TemperatureSensor.CurrentTemperature = convertUnits(this.deviceStatus.temperature as number, 'CELSIUS', (this.device as meterProConfig).convertUnitTo)
|
|
268
|
+
this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}`)
|
|
268
269
|
}
|
|
269
270
|
|
|
270
271
|
// Carbon Dioxide Sensor
|
|
@@ -367,7 +368,7 @@ export class MeterPro extends deviceBase {
|
|
|
367
368
|
if ((serviceData.model === SwitchBotBLEModel.MeterPro && serviceData.modelName === SwitchBotBLEModelName.MeterPro)
|
|
368
369
|
|| (serviceData.model === SwitchBotBLEModel.MeterProCO2 && serviceData.modelName === SwitchBotBLEModelName.MeterProCO2)) {
|
|
369
370
|
this.serviceData = serviceData
|
|
370
|
-
if (serviceData !== undefined
|
|
371
|
+
if (serviceData !== undefined && serviceData !== null) {
|
|
371
372
|
await this.BLEparseStatus()
|
|
372
373
|
await this.updateHomeKitCharacteristics()
|
|
373
374
|
} else {
|
|
@@ -393,7 +394,7 @@ export class MeterPro extends deviceBase {
|
|
|
393
394
|
this.platform.bleEventHandler[this.device.bleMac] = async (context: meterProServiceData) => {
|
|
394
395
|
try {
|
|
395
396
|
this.serviceData = context
|
|
396
|
-
if (context !== undefined
|
|
397
|
+
if (context !== undefined && context !== null) {
|
|
397
398
|
this.debugLog(`received BLE: ${JSON.stringify(context)}`)
|
|
398
399
|
await this.BLEparseStatus()
|
|
399
400
|
await this.updateHomeKitCharacteristics()
|
|
@@ -428,7 +429,7 @@ export class MeterPro extends deviceBase {
|
|
|
428
429
|
}
|
|
429
430
|
} catch (e: any) {
|
|
430
431
|
await this.apiError(e)
|
|
431
|
-
this.errorLog(`failed openAPIRefreshStatus with ${this.device.connectionType} Connection, Error
|
|
432
|
+
this.errorLog(`failed openAPIRefreshStatus with ${this.device.connectionType} Connection, Error: ${e.message ?? e}`)
|
|
432
433
|
}
|
|
433
434
|
}
|
|
434
435
|
|
|
@@ -438,7 +439,7 @@ export class MeterPro extends deviceBase {
|
|
|
438
439
|
this.platform.webhookEventHandler[this.device.deviceId] = async (context: meterProWebhookContext) => {
|
|
439
440
|
try {
|
|
440
441
|
this.webhookContext = context
|
|
441
|
-
if (context !== undefined
|
|
442
|
+
if (context !== undefined && context !== null) {
|
|
442
443
|
this.debugLog(`received Webhook: ${JSON.stringify(context)}`)
|
|
443
444
|
await this.parseStatusWebhook()
|
|
444
445
|
await this.updateHomeKitCharacteristics()
|
|
@@ -262,7 +262,7 @@ export class Motion extends deviceBase {
|
|
|
262
262
|
// Update HomeKit
|
|
263
263
|
if (serviceData.model === SwitchBotBLEModel.MotionSensor && serviceData.modelName === SwitchBotBLEModelName.MotionSensor) {
|
|
264
264
|
this.serviceData = serviceData
|
|
265
|
-
if (serviceData !== undefined
|
|
265
|
+
if (serviceData !== undefined && serviceData !== null) {
|
|
266
266
|
await this.BLEparseStatus()
|
|
267
267
|
await this.updateHomeKitCharacteristics()
|
|
268
268
|
} else {
|
|
@@ -288,7 +288,7 @@ export class Motion extends deviceBase {
|
|
|
288
288
|
this.platform.bleEventHandler[this.device.bleMac] = async (context: motionSensorServiceData) => {
|
|
289
289
|
try {
|
|
290
290
|
this.serviceData = context
|
|
291
|
-
if (context !== undefined
|
|
291
|
+
if (context !== undefined && context !== null) {
|
|
292
292
|
this.debugLog(`received BLE: ${JSON.stringify(context)}`)
|
|
293
293
|
await this.BLEparseStatus()
|
|
294
294
|
await this.updateHomeKitCharacteristics()
|
|
@@ -333,7 +333,7 @@ export class Motion extends deviceBase {
|
|
|
333
333
|
this.platform.webhookEventHandler[this.device.deviceId] = async (context: motionSensorWebhookContext) => {
|
|
334
334
|
try {
|
|
335
335
|
this.webhookContext = context
|
|
336
|
-
if (context !== undefined
|
|
336
|
+
if (context !== undefined && context !== null) {
|
|
337
337
|
this.debugLog(`received Webhook: ${JSON.stringify(context)}`)
|
|
338
338
|
await this.parseStatusWebhook()
|
|
339
339
|
await this.updateHomeKitCharacteristics()
|
package/src/devices-hap/plug.ts
CHANGED
|
@@ -187,7 +187,7 @@ export class Plug extends deviceBase {
|
|
|
187
187
|
if ((serviceData.model === SwitchBotBLEModel.PlugMiniUS || SwitchBotBLEModel.PlugMiniJP)
|
|
188
188
|
&& serviceData.modelName === (SwitchBotBLEModelName.PlugMini || SwitchBotBLEModelName.PlugMini)) {
|
|
189
189
|
this.serviceData = serviceData
|
|
190
|
-
if (serviceData !== undefined
|
|
190
|
+
if (serviceData !== undefined && serviceData !== null) {
|
|
191
191
|
await this.BLEparseStatus()
|
|
192
192
|
await this.updateHomeKitCharacteristics()
|
|
193
193
|
} else {
|
|
@@ -213,7 +213,7 @@ export class Plug extends deviceBase {
|
|
|
213
213
|
this.platform.bleEventHandler[this.device.bleMac] = async (context: plugMiniUSServiceData | plugMiniJPServiceData) => {
|
|
214
214
|
try {
|
|
215
215
|
this.serviceData = context
|
|
216
|
-
if (context !== undefined
|
|
216
|
+
if (context !== undefined && context !== null) {
|
|
217
217
|
this.debugLog(`received BLE: ${JSON.stringify(context)}`)
|
|
218
218
|
await this.BLEparseStatus()
|
|
219
219
|
await this.updateHomeKitCharacteristics()
|
|
@@ -258,7 +258,7 @@ export class Plug extends deviceBase {
|
|
|
258
258
|
this.platform.webhookEventHandler[this.device.deviceId] = async (context: plugWebhookContext | plugMiniUSWebhookContext | plugMiniJPWebhookContext) => {
|
|
259
259
|
try {
|
|
260
260
|
this.webhookContext = context
|
|
261
|
-
if (context !== undefined
|
|
261
|
+
if (context !== undefined && context !== null) {
|
|
262
262
|
this.debugLog(`received Webhook: ${JSON.stringify(context)}`)
|
|
263
263
|
await this.parseStatusWebhook()
|
|
264
264
|
await this.updateHomeKitCharacteristics()
|
|
@@ -320,10 +320,14 @@ export class Plug extends deviceBase {
|
|
|
320
320
|
return await this.retryBLE({
|
|
321
321
|
max: this.maxRetryBLE(),
|
|
322
322
|
fn: async () => {
|
|
323
|
-
if (
|
|
324
|
-
|
|
323
|
+
if (deviceList.length > 0) {
|
|
324
|
+
if (this.Outlet.On) {
|
|
325
|
+
return await deviceList[0].turnOn()
|
|
326
|
+
} else {
|
|
327
|
+
return await deviceList[0].turnOff()
|
|
328
|
+
}
|
|
325
329
|
} else {
|
|
326
|
-
|
|
330
|
+
throw new Error('No devices found during discovery.')
|
|
327
331
|
}
|
|
328
332
|
},
|
|
329
333
|
})
|
|
@@ -315,7 +315,7 @@ export class RelaySwitch extends deviceBase {
|
|
|
315
315
|
// Update HomeKit
|
|
316
316
|
if ((serviceData.model === SwitchBotBLEModel.RelaySwitch1PM && serviceData.modelName === SwitchBotBLEModelName.RelaySwitch1PM) ?? (serviceData.model === SwitchBotBLEModel.RelaySwitch1 && serviceData.modelName === SwitchBotBLEModelName.RelaySwitch1)) {
|
|
317
317
|
this.serviceData = serviceData
|
|
318
|
-
if (serviceData !== undefined
|
|
318
|
+
if (serviceData !== undefined && serviceData !== null) {
|
|
319
319
|
await this.BLEparseStatus()
|
|
320
320
|
await this.updateHomeKitCharacteristics()
|
|
321
321
|
} else {
|
|
@@ -341,7 +341,7 @@ export class RelaySwitch extends deviceBase {
|
|
|
341
341
|
this.platform.bleEventHandler[this.device.bleMac] = async (context: relaySwitch1ServiceData | relaySwitch1PMServiceData) => {
|
|
342
342
|
try {
|
|
343
343
|
this.serviceData = context
|
|
344
|
-
if (context !== undefined
|
|
344
|
+
if (context !== undefined && context !== null) {
|
|
345
345
|
this.debugLog(`received BLE: ${JSON.stringify(context)}`)
|
|
346
346
|
await this.BLEparseStatus()
|
|
347
347
|
await this.updateHomeKitCharacteristics()
|
|
@@ -386,7 +386,7 @@ export class RelaySwitch extends deviceBase {
|
|
|
386
386
|
this.platform.webhookEventHandler[this.device.deviceId] = async (context: relaySwitch1Context | relaySwitch1PMContext) => {
|
|
387
387
|
try {
|
|
388
388
|
this.webhookContext = context
|
|
389
|
-
if (context !== undefined
|
|
389
|
+
if (context !== undefined && context !== null) {
|
|
390
390
|
this.debugLog(`received Webhook: ${JSON.stringify(context)}`)
|
|
391
391
|
await this.parseStatusWebhook()
|
|
392
392
|
await this.updateHomeKitCharacteristics()
|
|
@@ -275,7 +275,7 @@ export class RobotVacuumCleaner extends deviceBase {
|
|
|
275
275
|
// Update HomeKit
|
|
276
276
|
if (serviceData.model === SwitchBotBLEModel.Unknown && serviceData.modelName === SwitchBotBLEModelName.Unknown) {
|
|
277
277
|
this.serviceData = serviceData
|
|
278
|
-
if (serviceData !== undefined
|
|
278
|
+
if (serviceData !== undefined && serviceData !== null) {
|
|
279
279
|
await this.BLEparseStatus()
|
|
280
280
|
await this.updateHomeKitCharacteristics()
|
|
281
281
|
} else {
|
|
@@ -301,7 +301,7 @@ export class RobotVacuumCleaner extends deviceBase {
|
|
|
301
301
|
this.platform.bleEventHandler[this.device.bleMac] = async (context: robotVacuumCleanerServiceData) => {
|
|
302
302
|
try {
|
|
303
303
|
this.serviceData = context
|
|
304
|
-
if (context !== undefined
|
|
304
|
+
if (context !== undefined && context !== null) {
|
|
305
305
|
this.debugLog(`received BLE: ${JSON.stringify(context)}`)
|
|
306
306
|
await this.BLEparseStatus()
|
|
307
307
|
await this.updateHomeKitCharacteristics()
|
|
@@ -346,7 +346,7 @@ export class RobotVacuumCleaner extends deviceBase {
|
|
|
346
346
|
this.platform.webhookEventHandler[this.device.deviceId] = async (context: robotVacuumCleanerS1WebhookContext | robotVacuumCleanerS1PlusWebhookContext | floorCleaningRobotS10WebhookContext) => {
|
|
347
347
|
try {
|
|
348
348
|
this.webhookContext = context
|
|
349
|
-
if (context !== undefined
|
|
349
|
+
if (context !== undefined && context !== null) {
|
|
350
350
|
this.debugLog(`received Webhook: ${JSON.stringify(context)}`)
|
|
351
351
|
await this.parseStatusWebhook()
|
|
352
352
|
await this.updateHomeKitCharacteristics()
|
|
@@ -400,14 +400,20 @@ export class RobotVacuumCleaner extends deviceBase {
|
|
|
400
400
|
switchBotBLE
|
|
401
401
|
.discover({ model: this.device.bleModel, id: this.device.bleMac })
|
|
402
402
|
.then(async (device_list: SwitchbotDevice[]) => {
|
|
403
|
+
const deviceList = device_list as any[]
|
|
403
404
|
this.infoLog(`On: ${this.LightBulb.On}`)
|
|
405
|
+
this.warnLog(`device_list: ${JSON.stringify(device_list)}`)
|
|
404
406
|
return await this.retryBLE({
|
|
405
407
|
max: this.maxRetryBLE(),
|
|
406
408
|
fn: async () => {
|
|
407
|
-
if (
|
|
408
|
-
|
|
409
|
+
if (deviceList && Array.isArray(deviceList) && deviceList.length > 0) {
|
|
410
|
+
if (this.LightBulb.On) {
|
|
411
|
+
return await (deviceList[0] as any).turnOn()
|
|
412
|
+
} else {
|
|
413
|
+
return await (deviceList[0] as any).turnOff()
|
|
414
|
+
}
|
|
409
415
|
} else {
|
|
410
|
-
|
|
416
|
+
throw new Error('No devices found during discovery.')
|
|
411
417
|
}
|
|
412
418
|
},
|
|
413
419
|
})
|
|
@@ -269,7 +269,7 @@ export class WaterDetector extends deviceBase {
|
|
|
269
269
|
// Update HomeKit
|
|
270
270
|
if (serviceData.model === SwitchBotBLEModel.Leak && serviceData.modelName === SwitchBotBLEModelName.Leak) {
|
|
271
271
|
this.serviceData = serviceData
|
|
272
|
-
if (serviceData !== undefined
|
|
272
|
+
if (serviceData !== undefined && serviceData !== null) {
|
|
273
273
|
await this.BLEparseStatus()
|
|
274
274
|
await this.updateHomeKitCharacteristics()
|
|
275
275
|
} else {
|
|
@@ -295,7 +295,7 @@ export class WaterDetector extends deviceBase {
|
|
|
295
295
|
this.platform.bleEventHandler[this.device.bleMac] = async (context: waterLeakDetectorServiceData) => {
|
|
296
296
|
try {
|
|
297
297
|
this.serviceData = context
|
|
298
|
-
if (context !== undefined
|
|
298
|
+
if (context !== undefined && context !== null) {
|
|
299
299
|
this.debugLog(`received BLE: ${JSON.stringify(context)}`)
|
|
300
300
|
await this.BLEparseStatus()
|
|
301
301
|
await this.updateHomeKitCharacteristics()
|
|
@@ -340,7 +340,7 @@ export class WaterDetector extends deviceBase {
|
|
|
340
340
|
this.platform.webhookEventHandler[this.device.deviceId] = async (context: waterLeakDetectorWebhookContext) => {
|
|
341
341
|
try {
|
|
342
342
|
this.webhookContext = context
|
|
343
|
-
if (context !== undefined
|
|
343
|
+
if (context !== undefined && context !== null) {
|
|
344
344
|
this.debugLog(`received Webhook: ${JSON.stringify(context)}`)
|
|
345
345
|
await this.parseStatusWebhook()
|
|
346
346
|
await this.updateHomeKitCharacteristics()
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
</form>
|
|
44
44
|
<table class="table w-100" id="deviceTable" style="display: none">
|
|
45
45
|
<thead>
|
|
46
|
-
<tr
|
|
46
|
+
<tr>
|
|
47
47
|
<th scope="col" style="width: 40%">Device Name</th>
|
|
48
48
|
<th scope="col" style="width: 60%" id="displayName"></th>
|
|
49
49
|
</tr>
|
|
@@ -69,6 +69,18 @@
|
|
|
69
69
|
<th scope="row">Connection Type</th>
|
|
70
70
|
<td id="connectionType"></td>
|
|
71
71
|
</tr>
|
|
72
|
+
<tr>
|
|
73
|
+
<th scope="row">Refresh Rate</th>
|
|
74
|
+
<td id="refreshRate"></td>
|
|
75
|
+
</tr>
|
|
76
|
+
<tr>
|
|
77
|
+
<th scope="row">Update Rate</th>
|
|
78
|
+
<td id="updateRate"></td>
|
|
79
|
+
</tr>
|
|
80
|
+
<tr>
|
|
81
|
+
<th scope="row">Push Rate</th>
|
|
82
|
+
<td id="pushRate"></td>
|
|
83
|
+
</tr>
|
|
72
84
|
</tbody>
|
|
73
85
|
</table>
|
|
74
86
|
<p class="text-center">External accessories will not be displayed here.</p>
|
package/src/platform-hap.ts
CHANGED
|
@@ -243,6 +243,7 @@ export class SwitchBotHAPPlatform implements DynamicPlatformPlugin {
|
|
|
243
243
|
dailyLimit: dailyApiLimit,
|
|
244
244
|
reserveForCommands: dailyApiReserveForCommands,
|
|
245
245
|
pausePollingAtReserve: webhookOnlyOnReserve,
|
|
246
|
+
resetAtLocalMidnight: this.config.options?.dailyApiResetAtLocalMidnight ?? false,
|
|
246
247
|
})
|
|
247
248
|
this.apiTracker.startHourlyLogging()
|
|
248
249
|
} catch (e: any) {
|
|
@@ -506,8 +507,12 @@ export class SwitchBotHAPPlatform implements DynamicPlatformPlugin {
|
|
|
506
507
|
const { response, statusCode } = await this.switchBotAPI.getDevices()
|
|
507
508
|
this.debugLog(`response: ${JSON.stringify(response)}`)
|
|
508
509
|
if (this.isSuccessfulResponse(statusCode)) {
|
|
509
|
-
|
|
510
|
-
|
|
510
|
+
const deviceList = Array.isArray(response.body.deviceList) ? response.body.deviceList : []
|
|
511
|
+
const irDeviceList = Array.isArray(response.body.infraredRemoteList) ? response.body.infraredRemoteList : []
|
|
512
|
+
await this.handleDevices(deviceList)
|
|
513
|
+
await this.handleIRDevices(irDeviceList)
|
|
514
|
+
// Diagnostic: warn users if their device count + refresh rate may exceed daily limits
|
|
515
|
+
this.validateApiUsageConfig(deviceList.length, irDeviceList.length)
|
|
511
516
|
break
|
|
512
517
|
} else {
|
|
513
518
|
// Check if rate limit exceeded (429)
|
|
@@ -601,6 +606,7 @@ export class SwitchBotHAPPlatform implements DynamicPlatformPlugin {
|
|
|
601
606
|
'Plug Mini (JP)': Plug,
|
|
602
607
|
'Smart Lock': Lock,
|
|
603
608
|
'Smart Lock Pro': Lock,
|
|
609
|
+
'Smart Lock Ultra': Lock,
|
|
604
610
|
'Color Bulb': ColorBulb,
|
|
605
611
|
'K10+': RobotVacuumCleaner,
|
|
606
612
|
'K10+ Pro': RobotVacuumCleaner,
|
|
@@ -723,12 +729,29 @@ export class SwitchBotHAPPlatform implements DynamicPlatformPlugin {
|
|
|
723
729
|
const allowConfigOnly = Boolean(this.config.options?.allowConfigOnlyDevices)
|
|
724
730
|
const devices = mergeByDeviceId(this.config.options.devices ?? [], devicesWithTemplates ?? [], allowConfigOnly)
|
|
725
731
|
|
|
732
|
+
// Apply global webhook option to devices that don't have their own webhook setting
|
|
733
|
+
if (this.config.options?.webhook === true) {
|
|
734
|
+
for (const device of devices) {
|
|
735
|
+
if (device.webhook === undefined) {
|
|
736
|
+
device.webhook = true
|
|
737
|
+
this.debugLog(`Applying global webhook option to device: ${device.deviceName ?? device.deviceId}`)
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
726
742
|
this.debugLog(`SwitchBot Devices: ${JSON.stringify(devices)}`)
|
|
727
743
|
|
|
728
744
|
for (const device of devices) {
|
|
729
745
|
if (device.configDeviceName) {
|
|
730
746
|
device.deviceName = device.configDeviceName
|
|
731
747
|
}
|
|
748
|
+
// Log effective webhook setting for diagnostics
|
|
749
|
+
try {
|
|
750
|
+
const effectiveWebhook = device.webhook !== undefined ? device.webhook : (this.config.options?.webhook === true ? true : undefined)
|
|
751
|
+
this.debugLog(`Effective webhook for device ${device.deviceName ?? device.deviceId}: ${String(effectiveWebhook)}`)
|
|
752
|
+
} catch (e: any) {
|
|
753
|
+
this.debugLog(`Failed logging effective webhook for ${device.deviceName ?? device.deviceId}: ${e?.message ?? e}`)
|
|
754
|
+
}
|
|
732
755
|
await this.createDevice(device)
|
|
733
756
|
}
|
|
734
757
|
}
|
|
@@ -768,11 +791,28 @@ export class SwitchBotHAPPlatform implements DynamicPlatformPlugin {
|
|
|
768
791
|
const allowConfigOnly = Boolean(this.config.options?.allowConfigOnlyDevices)
|
|
769
792
|
const devices = mergeByDeviceId(this.config.options.irdevices ?? [], devicesWithTemplates ?? [], allowConfigOnly)
|
|
770
793
|
|
|
794
|
+
// Apply global webhook option to IR devices that don't have their own webhook setting
|
|
795
|
+
if (this.config.options?.webhook === true) {
|
|
796
|
+
for (const device of devices) {
|
|
797
|
+
if (device.webhook === undefined) {
|
|
798
|
+
device.webhook = true
|
|
799
|
+
this.debugLog(`Applying global webhook option to IR device: ${device.deviceName ?? device.deviceId}`)
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
771
804
|
this.debugLog(`IR Devices: ${JSON.stringify(devices)}`)
|
|
772
805
|
for (const device of devices) {
|
|
773
806
|
if (device.configDeviceName) {
|
|
774
807
|
device.deviceName = device.configDeviceName
|
|
775
808
|
}
|
|
809
|
+
// Log effective webhook setting for diagnostics (IR devices)
|
|
810
|
+
try {
|
|
811
|
+
const effectiveWebhook = device.webhook !== undefined ? device.webhook : (this.config.options?.webhook === true ? true : undefined)
|
|
812
|
+
this.debugLog(`Effective webhook for IR device ${device.deviceName ?? device.deviceId}: ${String(effectiveWebhook)}`)
|
|
813
|
+
} catch (e: any) {
|
|
814
|
+
this.debugLog(`Failed logging effective webhook for IR ${device.deviceName ?? device.deviceId}: ${e?.message ?? e}`)
|
|
815
|
+
}
|
|
776
816
|
await this.createIRDevice(device)
|
|
777
817
|
}
|
|
778
818
|
}
|
|
@@ -814,6 +854,7 @@ export class SwitchBotHAPPlatform implements DynamicPlatformPlugin {
|
|
|
814
854
|
'Plug Mini (JP)': this.createPlug.bind(this),
|
|
815
855
|
'Smart Lock': this.createLock.bind(this),
|
|
816
856
|
'Smart Lock Pro': this.createLock.bind(this),
|
|
857
|
+
'Smart Lock Ultra': this.createLock.bind(this),
|
|
817
858
|
'Color Bulb': this.createColorBulb.bind(this),
|
|
818
859
|
'K10+': this.createRobotVacuumCleaner.bind(this),
|
|
819
860
|
'K10+ Pro': this.createRobotVacuumCleaner.bind(this),
|
|
@@ -1809,7 +1850,7 @@ export class SwitchBotHAPPlatform implements DynamicPlatformPlugin {
|
|
|
1809
1850
|
existingAccessory.context.device = device
|
|
1810
1851
|
existingAccessory.context.deviceId = device.deviceId
|
|
1811
1852
|
existingAccessory.context.deviceType = device.deviceType
|
|
1812
|
-
existingAccessory.context.model = device.deviceType === 'Smart Lock Pro' ? SwitchBotModel.LockPro : SwitchBotModel.Lock
|
|
1853
|
+
existingAccessory.context.model = (device.deviceType === 'Smart Lock Pro' || device.deviceType === 'Smart Lock Ultra') ? SwitchBotModel.LockPro : SwitchBotModel.Lock
|
|
1813
1854
|
existingAccessory.displayName = device.configDeviceName
|
|
1814
1855
|
? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
|
|
1815
1856
|
: await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName)
|
|
@@ -1835,7 +1876,7 @@ export class SwitchBotHAPPlatform implements DynamicPlatformPlugin {
|
|
|
1835
1876
|
accessory.context.device = device
|
|
1836
1877
|
accessory.context.deviceId = device.deviceId
|
|
1837
1878
|
accessory.context.deviceType = device.deviceType
|
|
1838
|
-
accessory.context.model = device.deviceType === 'Smart Lock Pro' ? SwitchBotModel.LockPro : SwitchBotModel.Lock
|
|
1879
|
+
accessory.context.model = (device.deviceType === 'Smart Lock Pro' || device.deviceType === 'Smart Lock Ultra') ? SwitchBotModel.LockPro : SwitchBotModel.Lock
|
|
1839
1880
|
accessory.displayName = device.configDeviceName
|
|
1840
1881
|
? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName)
|
|
1841
1882
|
: await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName)
|
|
@@ -2912,23 +2953,26 @@ export class SwitchBotHAPPlatform implements DynamicPlatformPlugin {
|
|
|
2912
2953
|
}
|
|
2913
2954
|
|
|
2914
2955
|
async retryRequest(device: (device & devicesConfig) | (irdevice & irDevicesConfig), deviceMaxRetries: number, deviceDelayBetweenRetries: number): Promise<{ response: any, statusCode: deviceStatusRequest['statusCode'] }> {
|
|
2956
|
+
// Check API budget BEFORE attempting any retries - don't waste cycles on blocked requests
|
|
2957
|
+
if (!this.apiTracker?.trySpend('poll')) {
|
|
2958
|
+
// Don't log on every blocked request - the ApiRequestTracker handles periodic warnings
|
|
2959
|
+
return {
|
|
2960
|
+
response: {
|
|
2961
|
+
deviceId: '',
|
|
2962
|
+
deviceType: '',
|
|
2963
|
+
hubDeviceId: '',
|
|
2964
|
+
version: 0,
|
|
2965
|
+
deviceName: '',
|
|
2966
|
+
},
|
|
2967
|
+
statusCode: 429,
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
|
|
2915
2971
|
let retryCount = 0
|
|
2916
2972
|
const maxRetries = deviceMaxRetries
|
|
2917
2973
|
const delayBetweenRetries = deviceDelayBetweenRetries
|
|
2918
2974
|
while (retryCount < maxRetries) {
|
|
2919
2975
|
try {
|
|
2920
|
-
if (!this.apiTracker?.trySpend('poll')) {
|
|
2921
|
-
return {
|
|
2922
|
-
response: {
|
|
2923
|
-
deviceId: '',
|
|
2924
|
-
deviceType: '',
|
|
2925
|
-
hubDeviceId: '',
|
|
2926
|
-
version: 0,
|
|
2927
|
-
deviceName: '',
|
|
2928
|
-
},
|
|
2929
|
-
statusCode: 429,
|
|
2930
|
-
}
|
|
2931
|
-
}
|
|
2932
2976
|
const { response, statusCode } = await this.switchBotAPI.getDeviceStatus(device.deviceId, this.config.credentials?.token, this.config.credentials?.secret)
|
|
2933
2977
|
this.debugLog(`response: ${JSON.stringify(response)}`)
|
|
2934
2978
|
return { response, statusCode }
|
|
@@ -3055,6 +3099,56 @@ export class SwitchBotHAPPlatform implements DynamicPlatformPlugin {
|
|
|
3055
3099
|
this.version = version
|
|
3056
3100
|
}
|
|
3057
3101
|
|
|
3102
|
+
/**
|
|
3103
|
+
* Validate that the user's configuration won't exceed API limits
|
|
3104
|
+
* Warn if device count × polling frequency will hit daily limits
|
|
3105
|
+
*/
|
|
3106
|
+
private validateApiUsageConfig(deviceCount: number, irDeviceCount: number): void {
|
|
3107
|
+
try {
|
|
3108
|
+
const totalDevices = deviceCount + irDeviceCount
|
|
3109
|
+
if (totalDevices === 0) {
|
|
3110
|
+
return
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
const refreshRate = this.platformRefreshRate ?? 300 // seconds
|
|
3114
|
+
const dailyLimit = this.config.options?.dailyApiLimit ?? 10000
|
|
3115
|
+
const reserveForCommands = this.config.options?.dailyApiReserveForCommands ?? 1000
|
|
3116
|
+
|
|
3117
|
+
// Calculate polls per day (86400 seconds in a day)
|
|
3118
|
+
const pollsPerDevicePerDay = Math.floor(86400 / refreshRate)
|
|
3119
|
+
const totalPollsPerDay = pollsPerDevicePerDay * totalDevices
|
|
3120
|
+
|
|
3121
|
+
// Add discovery calls (typically 1-2 per day)
|
|
3122
|
+
const estimatedDiscoveryCalls = 2
|
|
3123
|
+
const totalEstimatedCalls = totalPollsPerDay + estimatedDiscoveryCalls
|
|
3124
|
+
|
|
3125
|
+
const usableLimit = dailyLimit - reserveForCommands
|
|
3126
|
+
const percentOfLimit = Math.round((totalEstimatedCalls / usableLimit) * 100)
|
|
3127
|
+
|
|
3128
|
+
this.debugLog(`[API Usage Diagnostic] ${totalDevices} devices × ${pollsPerDevicePerDay} polls/day = ${totalPollsPerDay} estimated daily polls`)
|
|
3129
|
+
this.debugLog(`[API Usage Diagnostic] With ${reserveForCommands} reserved for commands, usable limit is ${usableLimit}`)
|
|
3130
|
+
|
|
3131
|
+
if (totalEstimatedCalls > dailyLimit) {
|
|
3132
|
+
this.errorLog(`⚠️ API LIMIT WARNING: Your configuration will exceed the daily API limit!`)
|
|
3133
|
+
this.errorLog(` Devices: ${totalDevices} | Refresh rate: ${refreshRate}s | Estimated daily polls: ${totalEstimatedCalls}`)
|
|
3134
|
+
this.errorLog(` Daily limit: ${dailyLimit} | You will use ${percentOfLimit}% of available budget`)
|
|
3135
|
+
this.errorLog(` SOLUTION: Increase refreshRate to ${Math.ceil((totalDevices * 86400) / usableLimit)} seconds or higher`)
|
|
3136
|
+
this.errorLog(` OR: Enable webhooks and set 'webhookOnlyOnReserve: true' to reduce polling`)
|
|
3137
|
+
} else if (totalEstimatedCalls > usableLimit) {
|
|
3138
|
+
this.warnLog(`⚠️ API USAGE WARNING: Configuration may exceed usable daily API budget`)
|
|
3139
|
+
this.warnLog(` Devices: ${totalDevices} | Refresh rate: ${refreshRate}s | Estimated daily polls: ${totalEstimatedCalls}`)
|
|
3140
|
+
this.warnLog(` Usable limit (after reserve): ${usableLimit} | You will use ${percentOfLimit}% of budget`)
|
|
3141
|
+
this.warnLog(` Polling may pause when approaching limit. Consider increasing refreshRate to ${Math.ceil((totalDevices * 86400) / usableLimit)}s`)
|
|
3142
|
+
} else if (percentOfLimit > 75) {
|
|
3143
|
+
this.infoLog(`[API Usage] Using ${percentOfLimit}% of daily budget (${totalEstimatedCalls}/${usableLimit} calls). Monitor usage if adding more devices.`)
|
|
3144
|
+
} else {
|
|
3145
|
+
this.debugLog(`[API Usage] Configuration looks good: ${percentOfLimit}% of daily budget (${totalEstimatedCalls}/${usableLimit} calls)`)
|
|
3146
|
+
}
|
|
3147
|
+
} catch (e: any) {
|
|
3148
|
+
this.debugErrorLog(`Failed to validate API usage config: ${e.message ?? e}`)
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3058
3152
|
/**
|
|
3059
3153
|
* Validate and clean a string value for a Name Characteristic.
|
|
3060
3154
|
* @param displayName - The display name of the accessory.
|