From b6770c695069b1153f2b0415a6592fd63a5d8420 Mon Sep 17 00:00:00 2001 From: Donavan Becker Date: Fri, 19 Nov 2021 23:15:59 -0600 Subject: [PATCH] Version 1.2.0 (#96) ## [Version 1.2.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v1.2.0) (2021-11-19) ## What's Changed * Added option to be able to do Bluetooth Low Energy (BLE) Only Connection. * Must supply `Device ID` & `Device Name` to the Device Config * Must Check `Enable Bluetooth Low Energy (BLE) Connection` * Fixed Bug: Air conditioner temperature not able to change. [#43](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/43) * Add option to set Min Lux and Max Lux for Curtain's Light Sensor. * Add `updateHomeKitCharacteristics` to IR Devices to contain all `updateCharacteristics` in one spot. * Add `Saturation` and `Hue` to Colorbulb. * Housekeeping and updated dependencies. **Full Changelog**: https://github.com/OpenWonderLabs/homebridge-switchbot/compare/v1.1.0...v1.2.0 --- CHANGELOG.md | 16 +- README.md | 93 +++-- config.schema.json | 116 ++++--- package-lock.json | 88 ++--- package.json | 16 +- src/devices/bots.ts | 327 ++++++++++-------- src/devices/colorbulb.ts | 309 +++++++++++++---- src/devices/contact.ts | 147 ++++---- src/devices/curtains.ts | 348 ++++++++++++------- src/devices/humidifiers.ts | 439 ++++++++++++++---------- src/devices/indoorcam.ts | 153 +++------ src/devices/meters.ts | 158 +++++---- src/devices/motion.ts | 100 +++--- src/devices/plugs.ts | 155 +++------ src/irdevices/airconditioners.ts | 164 +++++---- src/irdevices/airpurifiers.ts | 107 +++--- src/irdevices/cameras.ts | 69 ++-- src/irdevices/fans.ts | 111 +++--- src/irdevices/lights.ts | 74 ++-- src/irdevices/others.ts | 85 +++-- src/irdevices/tvs.ts | 134 ++++---- src/irdevices/vacuumcleaners.ts | 70 ++-- src/irdevices/waterheaters.ts | 73 ++-- src/platform.ts | 228 +++++++----- src/settings.ts | 571 ++++++++++++++++++++++++++++++- switchbot/contactsensors.json | 12 - switchbot/motionsensors.json | 11 - 27 files changed, 2632 insertions(+), 1542 deletions(-) delete mode 100644 switchbot/contactsensors.json delete mode 100644 switchbot/motionsensors.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a4d8145..ac204cd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,26 @@ All notable changes to this project will be documented in this file. This project uses [Semantic Versioning](https://semver.org/) +## [Version 1.2.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v1.2.0) (2021-11-19) + +## What's Changed +* Added option to be able to do Bluetooth Low Energy (BLE) Only Connection. + * Must supply `Device ID` & `Device Name` to the Device Config + * Must Check `Enable Bluetooth Low Energy (BLE) Connection` +* Fixed Bug: Air conditioner temperature not able to change. [#43](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/43) +* Add option to set Min Lux and Max Lux for Curtain's Light Sensor. +* Add `updateHomeKitCharacteristics` to IR Devices to contain all `updateCharacteristics` in one spot. +* Add `Saturation` and `Hue` to Colorbulb. +* Housekeeping and updated dependencies. + +**Full Changelog**: https://github.com/OpenWonderLabs/homebridge-switchbot/compare/v1.1.0...v1.2.0 + ## [Version 1.1.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v1.1.0) (2021-11-16) ## What's Changed * Fixed Bug: Curtains alternate between open/close state. [#85](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/85) * Fixed Bug: IR Fan won't be hidden in Home app. [#90](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/90) -* Fixed Bug: `hide_temperature` config option causing `Cannot read property 'updateCharacteristic' of undefined` for Humidifiers. [#90](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/90) +* Fixed Bug: `hide_temperature` config option causing `Cannot read property 'updateCharacteristic' of undefined` for Humidifiers. [#89](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/89) * Add option to Hide Curtain's Light Sensor. [#91](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/91) * Add option to Hide Contact Sensor's Motion Sensor or Light Sensor. diff --git a/README.md b/README.md index 0c7862f8..56f86c0b 100644 --- a/README.md +++ b/README.md @@ -23,16 +23,25 @@ plugin allows you to access your SwitchBot Device(s) from HomeKit with 3. Click **Install** ## Configuration +* ### If using OpenAPI Connection + 1. Download SwitchBot App on App Store or Google Play Store + 2. Register a SwitchBot account and log in into your account + 3. Generate an Open Token within the App + - Click Bottom Profile Tab + - Click Preference + - Click App version 10 Times, this will enable Developer Options + - Click Developer Options + - Click Copy to Clipboard + 4. Input your `openToken` into the config paramter +* ### If using BLE Connection + 1. Download SwitchBot App on App Store or Google Play Store + 2. Register a SwitchBot account and log in into your account + 3. Click on Device wanting to connect too plugin + - Click the Settings Gear + - Click Device Info + - Copy BLE Mac aka `deviceId` + 4. Input your `deviceId` into the Device Config -1. Download SwitchBot App on App Store or Google Play Store -2. Register a SwitchBot account and log in into your account -3. Generate an Open Token within the App - - Click Bottom Profile Tab - - Click Preference - - Click App version 10 Times, this will enable Developer Options - - Click Developer Options - - Click Copy to Clipboard -4. Input your `Token` into the config paramter

@@ -43,36 +52,68 @@ plugin allows you to access your SwitchBot Device(s) from HomeKit with ## Supported SwitchBot Devices - [SwitchBot Humidifier](https://www.switch-bot.com/products/switchbot-smart-humidifier) + - Supports OpenAPI & Bluetooth Low Energy (BLE) Connections + - Can Push Updates over OpenAPI + - Can Receive Udpates over BLE and OpenAPI - [SwitchBot Meter](https://www.switch-bot.com/products/switchbot-meter) - - [SwitchBot Hub Mini](https://www.switch-bot.com/products/switchbot-hub-mini) or [SwitchBot Hub Plus](https://www.switch-bot.com/products/switchbot-hub-plus) Required - - Enable Cloud Services for Device on SwitchBot App, If using OpenAPI. + - Supports OpenAPI & Bluetooth Low Energy (BLE) Connections + - If using OpenAPI: + - [SwitchBot Hub Mini](https://www.switch-bot.com/products/switchbot-hub-mini) or [SwitchBot Hub Plus](https://www.switch-bot.com/products/switchbot-hub-plus) Required + - Enable Cloud Services for Device on SwitchBot App + - If using Bluetooth Low Energy (BLE) only: + - Must supply `deviceId` & `deviceName` to Device Config + - Check `Enable Bluetooth Low Energy (BLE) Connection` on Device Config - [SwitchBot Motion Sensor](https://www.switch-bot.com/products/motion-sensor) - - [SwitchBot Hub Mini](https://www.switch-bot.com/products/switchbot-hub-mini) or [SwitchBot Hub Plus](https://www.switch-bot.com/products/switchbot-hub-plus) Required - - Enable Cloud Services for Device on SwitchBot App, If using OpenAPI. + - Supports OpenAPI & Bluetooth Low Energy (BLE) Connections + - If using OpenAPI: + - [SwitchBot Hub Mini](https://www.switch-bot.com/products/switchbot-hub-mini) or [SwitchBot Hub Plus](https://www.switch-bot.com/products/switchbot-hub-plus) Required + - Enable Cloud Services for Device on SwitchBot App + - If using Bluetooth Low Energy (BLE) only: + - Must supply `deviceId` & `deviceName` to Device Config + - Check `Enable Bluetooth Low Energy (BLE) Connection` on Device Config - [SwitchBot Contact Sensor](https://www.switch-bot.com/products/contact-sensor) - - [SwitchBot Hub Mini](https://www.switch-bot.com/products/switchbot-hub-mini) or [SwitchBot Hub Plus](https://www.switch-bot.com/products/switchbot-hub-plus) Required - - Enable Cloud Services for Device on SwitchBot App, If using OpenAPI. + - Supports OpenAPI & Bluetooth Low Energy (BLE) Connections + - If using OpenAPI: + - [SwitchBot Hub Mini](https://www.switch-bot.com/products/switchbot-hub-mini) or [SwitchBot Hub Plus](https://www.switch-bot.com/products/switchbot-hub-plus) Required + - Enable Cloud Services for Device on SwitchBot App + - If using Bluetooth Low Energy (BLE) only: + - Must supply `deviceId` & `deviceName` to Device Config + - Check `Enable Bluetooth Low Energy (BLE) Connection` on Device Config - [SwitchBot Curtain](https://www.switch-bot.com/products/switchbot-curtain) - - [SwitchBot Hub Mini](https://www.switch-bot.com/products/switchbot-hub-mini) or [SwitchBot Hub Plus](https://www.switch-bot.com/products/switchbot-hub-plus) Required - - Enable Cloud Services for Device on SwitchBot App, If using OpenAPI. + - Supports OpenAPI & Bluetooth Low Energy (BLE) Connections + - If using OpenAPI: + - [SwitchBot Hub Mini](https://www.switch-bot.com/products/switchbot-hub-mini) or [SwitchBot Hub Plus](https://www.switch-bot.com/products/switchbot-hub-plus) Required + - Enable Cloud Services for Device on SwitchBot App + - If using Bluetooth Low Energy (BLE) only: + - Must supply `deviceId` & `deviceName` to Device Config + - Check `Enable Bluetooth Low Energy (BLE) Connection` on Device Config - [SwitchBot Bulb](https://www.switch-bot.com/products/switchbot-color-bulb) - - [SwitchBot Hub Mini](https://www.switch-bot.com/products/switchbot-hub-mini) or [SwitchBot Hub Plus](https://www.switch-bot.com/products/switchbot-hub-plus) Required - - Enable Cloud Services for Device on SwitchBot App, If using OpenAPI. + - Supports OpenAPI Connection Only + - If using OpenAPI: + - [SwitchBot Hub Mini](https://www.switch-bot.com/products/switchbot-hub-mini) or [SwitchBot Hub Plus](https://www.switch-bot.com/products/switchbot-hub-plus) Required + - Enable Cloud Services for Device on SwitchBot App - [SwitchBot Bot](https://www.switch-bot.com/products/switchbot-bot) - - [SwitchBot Hub Mini](https://www.switch-bot.com/products/switchbot-hub-mini) or [SwitchBot Hub Plus](https://www.switch-bot.com/products/switchbot-hub-plus) Required - - Enable Cloud Services for Device on SwitchBot App, If using OpenAPI. - - You must set your Bot's Device ID in the Press Mode or Switch Mode Bot Settings (Advanced Settings > Bot Settings) - - Press Mode - Turns on then instantly turn it off - - Switch Mode - Turns on and keep it on until it is turned off - - This can get out of sync, since API doesn't give me a status - - To Correct you must go into the SwitchBot App and correct the status of either `On` or `Off` + - Supports OpenAPI & Bluetooth Low Energy (BLE) Connections + - If using OpenAPI: + - [SwitchBot Hub Mini](https://www.switch-bot.com/products/switchbot-hub-mini) or [SwitchBot Hub Plus](https://www.switch-bot.com/products/switchbot-hub-plus) Required + - Enable Cloud Services for Device on SwitchBot App + - You must set your Bot's Device ID in the Press Mode or Switch Mode Bot Settings (Advanced Settings > Device Settings > Bot Settings) + - Press Mode - Turns on then instantly turn it off + - Switch Mode - Turns on and keep it on until it is turned off + - This can get out of sync, since API doesn't give me a status + - To Correct you must go into the SwitchBot App and correct the status of either `On` or `Off` + - If using Bluetooth Low Energy (BLE) only: + - Must supply `deviceId` & `deviceName` to Device Config + - Check `Enable Bluetooth Low Energy (BLE) Connection` on Device Config - SwitchBot Plug + - Supports OpenAPI Connection Only ## Supported IR Devices ### _(All IR Devices require [Hub Plus](https://www.switch-bot.com/products/switchbot-hub-plus) or [Hub Mini](https://www.switch-bot.com/products/switchbot-hub-mini))_ - TV - Allows for On/Off and Volume Controls + - Optional Disable Sending Power Command - Projector (Displayed as TV) - Allows for On/Off and Volume Controls - Set Top Box (Displayed as Set Top Box) diff --git a/config.schema.json b/config.schema.json index a7c5445b..63649761 100644 --- a/config.schema.json +++ b/config.schema.json @@ -35,7 +35,6 @@ }, "options": { "type": "object", - "required": false, "properties": { "devices": { "type": "array", @@ -46,20 +45,26 @@ "deviceId": { "title": "Device ID", "type": "string", - "required": true, "placeholder": "81F3UT59513F" }, + "configDeviceName": { + "title": "Device Name", + "type": "string", + "placeholder": "Bot", + "condition": { + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].ble);" + } + }, "hide_device": { "title": "Hide Device", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].ble && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].ble);" } }, - "deviceType": { + "configDeviceType": { "title": "Device Type", "type": "string", - "required": true, "oneOf": [ { "title": "Bot", @@ -121,11 +126,10 @@ } }, "ble": { - "title": "Enable BLE Connection", + "title": "Enable Bluetooth Low Energy (BLE) Connection", "type": "boolean", - "required": true, "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device);" } }, "bot": { @@ -134,7 +138,6 @@ "mode": { "title": "Mode", "type": "string", - "required": true, "oneOf": [ { "title": "Press", @@ -150,30 +153,29 @@ } ], "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Bot' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Bot' && model.options.devices[arrayIndices].deviceId);" } }, "deviceType": { "title": "What Type of Device do you want to display in the Home App?", "type": "string", - "required": true, "oneOf": [ { - "title": "Switch", + "title": "Outlet", "enum": [ - "switch" + "outlet" ] }, { - "title": "Outlet", + "title": "Switch", "enum": [ - "outlet" + "switch" ] } ], - "default": "outlet", + "placeholder": "outlet", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Bot' && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].bot && model.options.devices[arrayIndices].bot.mode);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Bot' && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].bot && model.options.devices[arrayIndices].bot.mode);" } } } @@ -185,14 +187,14 @@ "title": "Hide Meter's Temperature Sensor", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Meter' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Meter' && model.options.devices[arrayIndices].deviceId);" } }, "hide_humidity": { "title": "Hide Meter's Humidity Sensor", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Meter' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Meter' && model.options.devices[arrayIndices].deviceId);" } }, "unit": { @@ -212,9 +214,8 @@ ] } ], - "required": false, "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Meter' && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].meter && !model.options.devices[arrayIndices].meter.hide_temperature);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Meter' && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].meter && !model.options.devices[arrayIndices].meter.hide_temperature);" } } } @@ -226,7 +227,7 @@ "title": "Hide Humidifier's Temperature Sensor", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Humidifier' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Humidifier' && model.options.devices[arrayIndices].deviceId);" } }, "set_minStep": { @@ -234,7 +235,7 @@ "type": "number", "placeholder": "1", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Humidifier' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Humidifier' && model.options.devices[arrayIndices].deviceId);" } } } @@ -246,14 +247,30 @@ "title": "Disable Grouping", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Curtain' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Curtain' && model.options.devices[arrayIndices].deviceId);" } }, "hide_lightsensor": { "title": "Hide Curtain's Light Sensor", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Curtain' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Curtain' && model.options.devices[arrayIndices].deviceId);" + } + }, + "set_minlux": { + "title": "Set Min Lux", + "type": "number", + "placeholder": "1", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Curtain' && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].curtain.hide_lightsensor);" + } + }, + "set_maxlux": { + "title": "Set Max Lux", + "type": "number", + "placeholder": "6001", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Curtain' && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].curtain.hide_lightsensor);" } }, "refreshRate": { @@ -263,7 +280,7 @@ "placeholder": 5, "description": "Indicates the number of seconds between before refreshing Curtain status while updating slide progress.", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Curtain' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Curtain' && model.options.devices[arrayIndices].deviceId);" } }, "set_minStep": { @@ -271,7 +288,7 @@ "type": "number", "placeholder": "1", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Curtain' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Curtain' && model.options.devices[arrayIndices].deviceId);" } }, "set_min": { @@ -279,7 +296,7 @@ "type": "number", "placeholder": "0", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Curtain' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Curtain' && model.options.devices[arrayIndices].deviceId);" } }, "set_max": { @@ -287,7 +304,7 @@ "type": "number", "placeholder": "100", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Curtain' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Curtain' && model.options.devices[arrayIndices].deviceId);" } } } @@ -299,14 +316,14 @@ "title": "Hide Curtains's Light Sensor", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Contact Sensor' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Contact Sensor' && model.options.devices[arrayIndices].deviceId);" } }, "hide_motionsensor": { "title": "Hide Curtains's Motion Sensor", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Contact Sensor' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Contact Sensor' && model.options.devices[arrayIndices].deviceId);" } } } @@ -319,7 +336,7 @@ "type": "number", "placeholder": "1", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceType === 'Color Bulb' && model.options.devices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Color Bulb' && model.options.devices[arrayIndices].deviceId);" } } } @@ -336,7 +353,6 @@ "deviceId": { "title": "Device ID", "type": "string", - "required": true, "placeholder": "81F3UT59513F" }, "hide_device": { @@ -346,10 +362,9 @@ "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId);" } }, - "remoteType": { + "configRemoteType": { "title": "Remote Type", "type": "string", - "required": true, "oneOf": [ { "title": "Air Conditioner (IR)", @@ -525,7 +540,7 @@ "title": "Hide Auto Mode on IR Air Conditioners", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].remoteType === 'Air Conditioner' || model.options.irdevices[arrayIndices].remoteType === 'DIY Air Conditioner') && model.options.irdevices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].configRemoteType === 'Air Conditioner' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Air Conditioner') && model.options.irdevices[arrayIndices].deviceId);" } } } @@ -537,14 +552,14 @@ "title": "Enable Swing Mode by Device ID", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].remoteType === 'Fan' || model.options.irdevices[arrayIndices].remoteType === 'DIY Fan') && model.options.irdevices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].configRemoteType === 'Fan' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Fan') && model.options.irdevices[arrayIndices].deviceId);" } }, "rotation_speed": { "title": "Enable Rotation Speed by Device ID", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].remoteType === 'Fan' || model.options.irdevices[arrayIndices].remoteType === 'DIY Fan') && model.options.irdevices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].configRemoteType === 'Fan' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Fan') && model.options.irdevices[arrayIndices].deviceId);" } }, "set_minStep": { @@ -552,7 +567,7 @@ "type": "number", "placeholder": "1", "condition": { - "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].remoteType === 'Fan' || model.options.irdevices[arrayIndices].remoteType === 'DIY Fan') && model.options.irdevices[arrayIndices].deviceId && model.options.irdevices[arrayIndices].irfan && !model.options.irdevices[arrayIndices].irfan.rotation_speed);" + "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].configRemoteType === 'Fan' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Fan') && model.options.irdevices[arrayIndices].deviceId && model.options.irdevices[arrayIndices].irfan && !model.options.irdevices[arrayIndices].irfan.rotation_speed);" } }, "set_min": { @@ -560,7 +575,7 @@ "type": "number", "placeholder": "0", "condition": { - "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].remoteType === 'Fan' || model.options.irdevices[arrayIndices].remoteType === 'DIY Fan') && model.options.irdevices[arrayIndices].deviceId && model.options.irdevices[arrayIndices].irfan && !model.options.irdevices[arrayIndices].irfan.rotation_speed);" + "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].configRemoteType === 'Fan' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Fan') && model.options.irdevices[arrayIndices].deviceId && model.options.irdevices[arrayIndices].irfan && !model.options.irdevices[arrayIndices].irfan.rotation_speed);" } }, "set_max": { @@ -568,7 +583,7 @@ "type": "number", "placeholder": "100", "condition": { - "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].remoteType === 'Fan' || model.options.irdevices[arrayIndices].remoteType === 'DIY Fan') && model.options.irdevices[arrayIndices].deviceId && model.options.irdevices[arrayIndices].irfan && !model.options.irdevices[arrayIndices].irfan.rotation_speed);" + "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].configRemoteType === 'Fan' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Fan') && model.options.irdevices[arrayIndices].deviceId && model.options.irdevices[arrayIndices].irfan && !model.options.irdevices[arrayIndices].irfan.rotation_speed);" } } } @@ -580,7 +595,7 @@ "title": "Disable Power Command", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].remoteType === 'TV' || model.options.irdevices[arrayIndices].remoteType === 'DIY TV' || model.options.irdevices[arrayIndices].remoteType === 'Projector' || model.options.irdevices[arrayIndices].remoteType === 'DIY Projector' || model.options.irdevices[arrayIndices].remoteType === 'Set Top Box' || model.options.irdevices[arrayIndices].remoteType === 'DIY Set Top Box' || model.options.irdevices[arrayIndices].remoteType === 'IPTV' || model.options.irdevices[arrayIndices].remoteType === 'DIY IPTV' || model.options.irdevices[arrayIndices].remoteType === 'DVD' || model.options.irdevices[arrayIndices].remoteType === 'DIY DVD' || model.options.irdevices[arrayIndices].remoteType === 'Speaker' || model.options.irdevices[arrayIndices].remoteType === 'DIY Speaker') && model.options.irdevices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].configRemoteType === 'TV' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY TV' || model.options.irdevices[arrayIndices].configRemoteType === 'Projector' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Projector' || model.options.irdevices[arrayIndices].configRemoteType === 'Set Top Box' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Set Top Box' || model.options.irdevices[arrayIndices].configRemoteType === 'IPTV' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY IPTV' || model.options.irdevices[arrayIndices].configRemoteType === 'DVD' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY DVD' || model.options.irdevices[arrayIndices].configRemoteType === 'Speaker' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Speaker') && model.options.irdevices[arrayIndices].deviceId);" } } } @@ -599,9 +614,8 @@ ] } ], - "required": false, "condition": { - "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].remoteType === 'Others' && model.options.irdevices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType === 'Others' && model.options.irdevices[arrayIndices].deviceId);" } }, "commandOn": { @@ -609,7 +623,7 @@ "type": "string", "placeholder": "On", "condition": { - "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].remoteType === 'Others' && model.options.irdevices[arrayIndices].deviceId && model.options.irdevices[arrayIndices].other && model.options.irdevices[arrayIndices].other.deviceType);" + "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType === 'Others' && model.options.irdevices[arrayIndices].deviceId && model.options.irdevices[arrayIndices].other && model.options.irdevices[arrayIndices].other.deviceType);" } }, "commandOff": { @@ -617,7 +631,7 @@ "type": "string", "placeholder": "Off", "condition": { - "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].remoteType === 'Others' && model.options.irdevices[arrayIndices].deviceId && model.options.irdevices[arrayIndices].other && !model.options.irdevices[arrayIndices].other.deviceType);" + "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType === 'Others' && model.options.irdevices[arrayIndices].deviceId && model.options.irdevices[arrayIndices].other && !model.options.irdevices[arrayIndices].other.deviceType);" } } } @@ -655,8 +669,7 @@ "device" ] } - ], - "required": false + ] } } } @@ -688,10 +701,11 @@ "displayFlex": true, "flex-direction": "column", "items": [ + "options.devices[].configDeviceName", "options.devices[].deviceId", "options.devices[].hide_device", - "options.devices[].deviceType", "options.devices[].ble", + "options.devices[].configDeviceType", "options.devices[].bot.mode", "options.devices[].bot.deviceType", "options.devices[].meter.hide_temperature", @@ -701,6 +715,8 @@ "options.devices[].humidifier.hide_temperature", "options.devices[].curtain.disable_group", "options.devices[].curtain.hide_lightsensor", + "options.devices[].curtain.set_minlux", + "options.devices[].curtain.set_maxlux", "options.devices[].curtain.refreshRate", "options.devices[].curtain.set_minStep", "options.devices[].curtain.set_min", @@ -730,7 +746,7 @@ "items": [ "options.irdevices[].deviceId", "options.irdevices[].hide_device", - "options.irdevices[].remoteType", + "options.irdevices[].configRemoteType", "options.irdevices[].irair.hide_automode", "options.irdevices[].irfan.rotation_speed", "options.irdevices[].irfan.swing_mode", diff --git a/package-lock.json b/package-lock.json index 075a4984..21d9e9a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@switchbot/homebridge-switchbot", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@switchbot/homebridge-switchbot", - "version": "1.1.0", + "version": "1.2.0", "funding": [ { "type": "Paypal", @@ -21,7 +21,7 @@ "rxjs": "^7.4.0" }, "devDependencies": { - "@types/node": "^16.11.7", + "@types/node": "^16.11.9", "@typescript-eslint/eslint-plugin": "^5.4.0", "@typescript-eslint/parser": "^5.4.0", "eslint": "^8.2.0", @@ -30,7 +30,7 @@ "npm-check-updates": "^12.0.2", "rimraf": "^3.0.2", "ts-node": "^10.4.0", - "typescript": "^4.4.4", + "typescript": "^4.5.2", "typescript-axios-wb": "^1.0.3" }, "engines": { @@ -405,9 +405,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.11.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", - "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==", + "version": "16.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.9.tgz", + "integrity": "sha512-MKmdASMf3LtPzwLyRrFjtFFZ48cMf8jmX5VRYrDQiJa8Ybu5VAmkqBWqKU8fdCwD8ysw4mQ9nrEHvzg6gunR7A==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { @@ -574,9 +574,9 @@ "devOptional": true }, "node_modules/acorn": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", - "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", + "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1072,15 +1072,16 @@ } }, "node_modules/cli-table": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz", - "integrity": "sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.8.tgz", + "integrity": "sha512-5IO15fJRzgM+hHZjvQlqD6UPRuVGWR4Ny5ZzaM5VJxJEQqSIEVyVh9dMAUN6CBAVfMc4/6CFEzbhnRftLRCyug==", "dev": true, "dependencies": { - "colors": "1.0.3" + "colors": "1.0.3", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">= 0.2.0" + "node": ">= 12" } }, "node_modules/clone-response": { @@ -4583,9 +4584,9 @@ } }, "node_modules/signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", "devOptional": true }, "node_modules/sisteransi": { @@ -4651,9 +4652,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.20", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", - "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "dependencies": { "buffer-from": "^1.0.0", @@ -4969,9 +4970,9 @@ } }, "node_modules/typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -5617,9 +5618,9 @@ "dev": true }, "@types/node": { - "version": "16.11.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz", - "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw==", + "version": "16.11.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.9.tgz", + "integrity": "sha512-MKmdASMf3LtPzwLyRrFjtFFZ48cMf8jmX5VRYrDQiJa8Ybu5VAmkqBWqKU8fdCwD8ysw4mQ9nrEHvzg6gunR7A==", "dev": true }, "@typescript-eslint/eslint-plugin": { @@ -5712,9 +5713,9 @@ "devOptional": true }, "acorn": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", - "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", + "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", "dev": true }, "acorn-jsx": { @@ -6094,12 +6095,13 @@ "dev": true }, "cli-table": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz", - "integrity": "sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.8.tgz", + "integrity": "sha512-5IO15fJRzgM+hHZjvQlqD6UPRuVGWR4Ny5ZzaM5VJxJEQqSIEVyVh9dMAUN6CBAVfMc4/6CFEzbhnRftLRCyug==", "dev": true, "requires": { - "colors": "1.0.3" + "colors": "1.0.3", + "strip-ansi": "^6.0.1" } }, "clone-response": { @@ -8745,9 +8747,9 @@ } }, "signal-exit": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.5.tgz", - "integrity": "sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", "devOptional": true }, "sisteransi": { @@ -8796,9 +8798,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.20", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", - "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -9033,9 +9035,9 @@ } }, "typescript": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", - "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", "dev": true }, "typescript-axios-wb": { diff --git a/package.json b/package.json index 531eb842..76830a1c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "displayName": "Homebridge SwitchBot", "name": "@switchbot/homebridge-switchbot", - "version": "1.1.0", + "version": "1.2.0", "description": "The [Homebridge](https://homebridge.io) SwitchBot plugin allows you to access your [SwitchBot](https://www.switch-bot.com) device(s) from HomeKit.", "author": "SwitchBot (https://github.com/SwitchBot)", "license": "ISC", @@ -47,22 +47,22 @@ "ir" ], "dependencies": { + "@homebridge/plugin-ui-utils": "^0.0.19", "axios": "^0.24.0", - "rxjs": "^7.4.0", "node-switchbot": "^1.1.2", - "@homebridge/plugin-ui-utils": "^0.0.19" + "rxjs": "^7.4.0" }, "devDependencies": { - "@types/node": "^16.11.7", + "@types/node": "^16.11.9", "@typescript-eslint/eslint-plugin": "^5.4.0", "@typescript-eslint/parser": "^5.4.0", "eslint": "^8.2.0", "homebridge": "^1.3.6", "nodemon": "^2.0.15", + "npm-check-updates": "^12.0.2", "rimraf": "^3.0.2", "ts-node": "^10.4.0", - "typescript": "^4.4.4", - "typescript-axios-wb": "^1.0.3", - "npm-check-updates": "^12.0.2" + "typescript": "^4.5.2", + "typescript-axios-wb": "^1.0.3" } -} +} \ No newline at end of file diff --git a/src/devices/bots.ts b/src/devices/bots.ts index 0c94f41e..f31cb2b0 100644 --- a/src/devices/bots.ts +++ b/src/devices/bots.ts @@ -2,7 +2,7 @@ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; import { interval, Subject } from 'rxjs'; import { debounceTime, skipWhile, tap } from 'rxjs/operators'; -import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse } from '../settings'; +import { DeviceURL, device, devicesConfig, serviceData, ad, switchbot, deviceStatusResponse, payload } from '../settings'; import { AxiosResponse } from 'axios'; /** @@ -31,6 +31,10 @@ export class Bot { state!: serviceData['state']; battery!: serviceData['battery']; + // Config + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; + // Updates botUpdateInProgress!: boolean; doBotUpdate!: Subject; @@ -41,8 +45,8 @@ export class Bot { public device: device & devicesConfig, ) { // Bot Config - this.platform.device(`[Bot Config] ble: ${device.ble}, mode: ${device.bot?.mode},` - + ` deviceType: ${device.bot?.deviceType}`); + this.platform.device(`Bot: ${this.accessory.displayName} Config: (ble: ${device.ble}, mode: ${device.bot?.mode},` + + ` deviceType: ${device.bot?.deviceType})`); // default placeholders this.SwitchOn = false; @@ -65,39 +69,36 @@ export class Bot { // get the LightBulb service if it exists, otherwise create a new LightBulb service // you can create multiple services for each accessory - switch (device.bot!.deviceType) { - case 'switch': - // If outletService still pressent, then remove first - if (this.outletService) { - this.platform.device('Removing Leftover outletService first'); - } - this.outletService = this.accessory.getService(this.platform.Service.Outlet); - accessory.removeService(this.outletService!); + if (device.bot?.deviceType === 'switch') { + // If outletService still pressent, then remove first + if (this.outletService) { + this.platform.device(`Bot: ${this.accessory.displayName} Removing Leftover outletService first`); + } + this.outletService = this.accessory.getService(this.platform.Service.Outlet); + accessory.removeService(this.outletService!); - // Add switchService - (this.switchService = - accessory.getService(this.platform.Service.Switch) || - accessory.addService(this.platform.Service.Switch)), `${accessory.displayName} Switch`; - this.platform.log.info('Displaying as Switch'); + // Add switchService + (this.switchService = + accessory.getService(this.platform.Service.Switch) || + accessory.addService(this.platform.Service.Switch)), `${accessory.displayName} Switch`; + this.platform.log.info(`Bot: ${this.accessory.displayName} Displaying as Switch`); - this.switchService.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); - break; - case 'outlet': - default: - // If switchService still pressent, then remove first - if (this.switchService) { - this.platform.device('Removing Leftover switchService first'); - } - this.switchService = this.accessory.getService(this.platform.Service.Switch); - accessory.removeService(this.switchService!); + this.switchService?.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + } else { + // If switchService still pressent, then remove first + if (this.switchService) { + this.platform.device(`Bot: ${this.accessory.displayName} Removing Leftover switchService first`); + } + this.switchService = this.accessory.getService(this.platform.Service.Switch); + accessory.removeService(this.switchService!); - // Add outletService - (this.outletService = - accessory.getService(this.platform.Service.Outlet) || - accessory.addService(this.platform.Service.Outlet)), `${accessory.displayName} Outlet`; - this.platform.log.info('Displaying as Outlet'); + // Add outletService + (this.outletService = + accessory.getService(this.platform.Service.Outlet) || + accessory.addService(this.platform.Service.Outlet)), `${accessory.displayName} Outlet`; + this.platform.log.info(`Bot: ${this.accessory.displayName} Displaying as Outlet`); - this.outletService.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); + this.outletService?.setCharacteristic(this.platform.Characteristic.Name, accessory.displayName); } if (device.ble) { @@ -141,9 +142,15 @@ export class Bot { try { await this.pushChanges(); } catch (e: any) { - this.platform.log.error(JSON.stringify(e.message)); - this.platform.debug(`Bot ${accessory.displayName} - ${JSON.stringify(e)}`); - this.apiError(e); + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges`); + if (this.deviceDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges,` + + ` Error: ${JSON.stringify(e)}`); + } } this.botUpdateInProgress = false; }); @@ -154,19 +161,17 @@ export class Bot { */ async parseStatus() { if (this.device.ble) { - this.platform.device('BLE'); await this.BLEparseStatus(); } else { - this.platform.device('OpenAPI'); await this.openAPIparseStatus(); } } private async BLEparseStatus() { - this.platform.debug('Bots BLE Device parseStatus'); + this.platform.debug(`Bot: ${this.accessory.displayName} BLE parseStatus`); // BLEmode (true if Switch Mode) | (false if Press Mode) if (this.mode) { - this.platform.device(`Switch Mode, mode: ${JSON.stringify(this.mode)}`); + this.platform.device(`Bot: ${this.accessory.displayName} Switch Mode, mode: ${JSON.stringify(this.mode)}`); } this.SwitchOn = Boolean(this.state); this.BatteryLevel = Number(this.battery); @@ -178,14 +183,17 @@ export class Bot { if (Number.isNaN(this.BatteryLevel)) { this.BatteryLevel = 100; } - this.platform.debug(`Bot ${this.accessory.displayName} On: ${this.SwitchOn}, BatteryLevel: ${this.BatteryLevel}`); + this.platform.debug(`Bot: ${this.accessory.displayName} On: ${this.SwitchOn}, BatteryLevel: ${this.BatteryLevel}`); } private async openAPIparseStatus() { - if (this.device.bot?.mode === 'press') { - this.SwitchOn = false; + if (this.platform.config.credentials?.openToken) { + this.platform.debug(`Bot: ${this.accessory.displayName} OpenAPI parseStatus`); + if (this.device.bot?.mode === 'press') { + this.SwitchOn = false; + } + this.platform.debug(`Bot ${this.accessory.displayName} On: ${this.SwitchOn}`); } - this.platform.debug(`Bot ${this.accessory.displayName} On: ${this.SwitchOn}`); } /** @@ -193,10 +201,8 @@ export class Bot { */ async refreshStatus() { if (this.device.ble) { - this.platform.device('BLE'); await this.BLERefreshStatus(); } else { - this.platform.device('OpenAPI'); await this.openAPIRefreshStatus(); } } @@ -208,12 +214,12 @@ export class Bot { const colon = this.device.deviceId!.match(/.{1,2}/g); const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A; this.device.bleMac = bleMac.toLowerCase(); - this.platform.device(this.device.bleMac!); + this.platform.device(`Bot: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); return switchbot; } private async BLERefreshStatus() { - this.platform.device('Bot BLE Device refreshStatus'); + this.platform.debug(`Bot: ${this.accessory.displayName} BLE refreshStatus`); // eslint-disable-next-line @typescript-eslint/no-var-requires const switchbot = this.connectBLE(); // Start to monitor advertisement packets @@ -227,9 +233,9 @@ export class Bot { this.mode = ad.serviceData.mode; this.state = ad.serviceData.state; this.battery = ad.serviceData.battery; - this.platform.device(`${this.device.bleMac}: ${JSON.stringify(ad.serviceData)}`); - this.platform.device(`${this.accessory.displayName}, Model: ${ad.serviceData.model}, Model Name: ${ad.serviceData.modelName},` - + ` Mode: ${ad.serviceData.mode}, State: ${ad.serviceData.state}, Battery: ${ad.serviceData.battery}`); + this.platform.device(`Bot: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); + this.platform.device(`Bot: ${this.accessory.displayName}, model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName},` + + ` mode: ${ad.serviceData.mode}, state: ${ad.serviceData.state}, battery: ${ad.serviceData.battery}`); }; // Wait 10 seconds return switchbot.wait(10000); @@ -239,30 +245,50 @@ export class Bot { this.parseStatus(); this.updateHomeKitCharacteristics(); }).catch(async (e: any) => { - this.platform.log.error(`BLE Connection Failed: ${e.message}`); - this.platform.log.warn('Using OpenAPI Connection'); - await this.openAPIRefreshStatus(); + this.platform.log.error(`Bot: ${this.accessory.displayName} failed refreshStatus with BLE Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + if (this.platform.config.credentials?.openToken) { + this.platform.log.warn(`Bot: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.openAPIRefreshStatus(); + } }); } private async openAPIRefreshStatus() { - try { - this.deviceStatus = { - statusCode: 100, - body: { - deviceId: this.device.deviceId!, - deviceType: this.device.deviceType!, - hubDeviceId: this.device.hubDeviceId, - power: 'on', - }, - message: 'success', - }; - this.parseStatus(); - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.platform.log.error(`Bot ${this.accessory.displayName} failed to refresh status, Error Message: ${JSON.stringify(e.message)}`); - this.platform.debug(`Bot ${this.accessory.displayName}, Error: ${JSON.stringify(e)}`); - this.apiError(e); + if (this.platform.config.credentials?.openToken) { + this.platform.debug(`Bot: ${this.accessory.displayName} OpenAPI refreshStatus`); + try { + this.deviceStatus = { + statusCode: 100, + body: { + deviceId: this.device.deviceId!, + deviceType: this.device.deviceType!, + hubDeviceId: this.device.hubDeviceId, + power: 'on', + }, + message: 'success', + }; + this.parseStatus(); + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + this.apiError(e); + } } } @@ -275,40 +301,62 @@ export class Bot { */ async pushChanges() { if (this.device.ble) { - this.platform.device('BLE'); await this.BLEpushChanges(); } else { - this.platform.device('OpenAPI'); await this.openAPIpushChanges(); } this.refreshStatus(); } private async BLEpushChanges() { - this.platform.device('Bot BLE Device pushChanges'); + this.platform.debug(`Bot: ${this.accessory.displayName} BLE pushChanges`); const switchbot = this.connectBLE(); if (this.device.bot?.mode === 'press') { - this.platform.device(`Press Mode: ${this.device.bot?.mode}`); + this.platform.device(`Bot: ${this.accessory.displayName} Press Mode: ${this.device.bot?.mode}`); switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }).then((device_list) => { - this.platform.log.info(`${this.accessory.displayName}, On: ${this.SwitchOn}`); + this.platform.log.info(`Bot: ${this.accessory.displayName}, On: ${this.SwitchOn}`); return device_list[0].press({ id: this.device.bleMac }); }).then(() => { - this.platform.device('Done.'); - }).catch((e: any) => { - this.platform.log.error(`BLE pushChanges Error Message: ${e.message}`); + this.platform.device(`Bot: ${this.accessory.displayName} Done.`); + }).catch(async (e: any) => { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + if (this.platform.config.credentials?.openToken) { + this.platform.log.warn(`Bot: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.openAPIpushChanges(); + } }); } else if (this.device.bot?.mode === 'switch') { - this.platform.device(`Press Mode: ${this.device.bot?.mode}`); - switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }).then((device_list) => { - this.platform.log.info(`${this.accessory.displayName}, On: ${this.SwitchOn}`); + this.platform.device(`Bot: ${this.accessory.displayName} Press Mode: ${this.device.bot?.mode}`); + switchbot.discover({ model: 'H', quick: true, id: this.device.bleMac }).then((device_list: any) => { + this.platform.log.info(`Bot: ${this.accessory.displayName} On: ${this.SwitchOn}`); return this.turnOnOff(device_list); }).then(() => { - this.platform.device('Done.'); - }).catch((e: any) => { - this.platform.log.error(`BLE pushChanges Error Message: ${e.message}`); + this.platform.device(`Bot: ${this.accessory.displayName} Done.`); + }).catch(async (e: any) => { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + if (this.platform.config.credentials?.openToken) { + this.platform.log.warn(`Bot: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.openAPIpushChanges(); + } }); } else { - this.platform.log.error('Mode Not Set.'); + this.platform.log.error(`Bot: ${this.accessory.displayName} Mode Not Set, mode: ${this.device.bot?.mode}`); } } @@ -321,43 +369,49 @@ export class Bot { } private async openAPIpushChanges() { - const payload = { - commandType: 'command', - parameter: 'default', - } as any; - - if (this.device.bot?.mode === 'switch' && this.SwitchOn) { - payload.command = 'turnOn'; - this.SwitchOn = true; - this.platform.debug(`Switch Mode, Turning ${this.SwitchOn}`); - } else if (this.device.bot?.mode === 'switch' && !this.SwitchOn) { - payload.command = 'turnOff'; - this.SwitchOn = false; - this.platform.debug(`Switch Mode, Turning ${this.SwitchOn}`); - } else if (this.device.bot?.mode === 'press') { - payload.command = 'press'; - this.platform.debug('Press Mode'); - this.SwitchOn = false; - } else { - throw new Error('Bot Device Paramters not set for this Bot.'); - } + if (this.platform.config.credentials?.openToken) { + try { + this.platform.debug(`Bot: ${this.accessory.displayName} OpenAPI pushChanges`); + const payload = { + commandType: 'command', + parameter: 'default', + } as payload; + + if (this.device.bot?.mode === 'switch' && this.SwitchOn) { + payload.command = 'turnOn'; + this.SwitchOn = true; + this.platform.debug(`Bot: ${this.accessory.displayName} Switch Mode, Turning ${this.SwitchOn}`); + } else if (this.device.bot?.mode === 'switch' && !this.SwitchOn) { + payload.command = 'turnOff'; + this.SwitchOn = false; + this.platform.debug(`Bot: ${this.accessory.displayName} Switch Mode, Turning ${this.SwitchOn}`); + } else if (this.device.bot?.mode === 'press') { + payload.command = 'press'; + this.platform.debug(`Bot: ${this.accessory.displayName} Press Mode`); + this.SwitchOn = false; + } else { + throw new Error(`Bot: ${this.accessory.displayName} Device Paramters not set for this Bot.`); + } - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`Bot ${this.accessory.displayName} pushchanges: ${JSON.stringify(payload)}`); - - // Make the API request - const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); - this.platform.debug(`Bot ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`); - this.statusCode(push); + this.platform.log.info(`Bot: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); + + // Make the API request + const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); + this.platform.debug(`Bot ${this.accessory.displayName} pushchanges: ${JSON.stringify(push.data)}`); + this.statusCode(push); + } catch (e: any) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Bot: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + } + } } /** @@ -365,27 +419,27 @@ export class Bot { */ updateHomeKitCharacteristics() { if (this.SwitchOn === undefined) { - this.platform.debug(`Bot ${this.accessory.displayName} On: ${this.SwitchOn}`); + this.platform.debug(`Bot: ${this.accessory.displayName} On: ${this.SwitchOn}`); } else { if (this.device.bot?.deviceType === 'switch') { this.switchService!.updateCharacteristic(this.platform.Characteristic.On, this.SwitchOn); } else { this.outletService!.updateCharacteristic(this.platform.Characteristic.On, this.SwitchOn); } - this.platform.device(`Bot ${this.accessory.displayName} updateCharacteristic On: ${this.SwitchOn}`); + this.platform.device(`Bot: ${this.accessory.displayName} updateCharacteristic On: ${this.SwitchOn}`); } if (this.device.ble) { if (this.BatteryLevel === undefined) { - this.platform.debug(`Bot ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + this.platform.debug(`Bot: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); } else { this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel); - this.platform.device(`Bot ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); + this.platform.device(`Bot: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); } if (this.StatusLowBattery === undefined) { - this.platform.debug(`Bot ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + this.platform.debug(`Bot: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); } else { this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.platform.device(`Bot ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); + this.platform.device(`Bot: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); } } } @@ -405,28 +459,29 @@ export class Bot { private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`Bot: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`Bot: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`Bot: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`Bot: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`Bot: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`Bot: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - this.platform.debug('Command successfully sent.'); + this.platform.debug(`Bot: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`Bot: ${this.accessory.displayName} Unknown statusCode.`); } } @@ -434,7 +489,7 @@ export class Bot { * Handle requests to set the "On" characteristic */ private handleOnSet(value: CharacteristicValue) { - this.platform.debug(`Bot ${this.accessory.displayName} - Set On: ${value}`); + this.platform.debug(`Bot: ${this.accessory.displayName} On: ${value}`); this.SwitchOn = value; this.doBotUpdate.next(); } diff --git a/src/devices/colorbulb.ts b/src/devices/colorbulb.ts index de71cf17..98c4d025 100644 --- a/src/devices/colorbulb.ts +++ b/src/devices/colorbulb.ts @@ -2,7 +2,7 @@ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; import { interval, Subject } from 'rxjs'; import { debounceTime, skipWhile, tap } from 'rxjs/operators'; -import { DeviceURL, device, devicesConfig, switchbot, deviceStatusResponse } from '../settings'; +import { DeviceURL, device, devicesConfig, switchbot, deviceStatusResponse, payload, hs2rgb, rgb2hs } from '../settings'; import { AxiosResponse } from 'axios'; /** @@ -16,7 +16,9 @@ export class ColorBulb { // Characteristic Values On!: CharacteristicValue; + Hue!: CharacteristicValue; Brightness!: CharacteristicValue; + Saturation!: CharacteristicValue; ColorTemperature!: CharacteristicValue; // OpenAPI Others @@ -27,6 +29,8 @@ export class ColorBulb { // Config set_minStep?: number; + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; // Updates colorBulbUpdateInProgress!: boolean; @@ -38,20 +42,11 @@ export class ColorBulb { public device: device & devicesConfig, ) { // ColorBulb Config - this.platform.device(`[ColorBulb Config] ble: ${device.ble}, set_minStep: ${device.colorbulb?.set_minStep}`); + this.platform.device(`Color Bulb: ${this.accessory.displayName} Config: (ble: ${device.ble}, set_minStep: ${device.colorbulb?.set_minStep}`); // default placeholders this.On = false; this.Brightness = 0; - if (device.ble) { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const SwitchBot = require('node-switchbot'); - this.switchbot = new SwitchBot(); - const colon = device.deviceId!.match(/.{1,2}/g); - const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A; - this.device.bleMac = bleMac.toLowerCase(); - this.platform.device(this.device.bleMac.toLowerCase()); - } // this is subject we use to track when we need to POST changes to the SwitchBot API this.doColorBulbUpdate = new Subject(); @@ -102,15 +97,38 @@ export class ColorBulb { // handle ColorTemperature events using the ColorTemperature characteristic this.service.getCharacteristic(this.platform.Characteristic.ColorTemperature) .setProps({ - minStep: this.minStep(), + minValue: 140, + maxValue: 500, + validValueRanges: [140, 500], + }) + .onGet(() => { + return this.ColorTemperature; + }) + .onSet(this.ColorTemperatureSet.bind(this)); + + // handle Hue events using the Hue characteristic + this.service.getCharacteristic(this.platform.Characteristic.Hue) + .setProps({ + minValue: 0, + maxValue: 360, + validValueRanges: [0, 360], + }) + .onGet(() => { + return this.Hue; + }) + .onSet(this.HueSet.bind(this)); + + // handle Hue events using the Hue characteristic + this.service.getCharacteristic(this.platform.Characteristic.Saturation) + .setProps({ minValue: 0, maxValue: 100, validValueRanges: [0, 100], }) .onGet(() => { - return this.ColorTemperature; + return this.Saturation; }) - .onSet(this.ColorTemperatureSet.bind(this)); + .onSet(this.SaturationSet.bind(this)); // Update Homekit this.updateHomeKitCharacteristics(); @@ -135,8 +153,15 @@ export class ColorBulb { try { await this.pushChanges(); } catch (e: any) { - this.platform.log.error(JSON.stringify(e.message)); - this.platform.debug(`Color Bulb ${accessory.displayName} - ${JSON.stringify(e)}`); + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} failed pushChanges`); + if (this.deviceDebug) { + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} failed pushChanges,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} failed pushChanges,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } this.colorBulbUpdateInProgress = false; @@ -160,116 +185,235 @@ export class ColorBulb { default: this.On = false; } - this.platform.debug(`Color Bulb ${this.accessory.displayName} On: ${this.On}`); - this.deviceStatus.body.brightness = Number(this.Brightness); - this.deviceStatus.body.colorTemperature = Number(this.ColorTemperature); + this.platform.log.warn(`Color Bulb: ${this.accessory.displayName} On: ${this.On}`); + + // Brightness + this.Brightness = Number(this.deviceStatus.body.brightness); + this.platform.log.warn(`Color Bulb: ${this.accessory.displayName} Brightness: ${this.Brightness}`); + + // Color, Hue & Brightness + if (this.deviceStatus.body.color) { + this.platform.log.warn(`Color Bulb: ${this.accessory.displayName} color: ${JSON.stringify(this.deviceStatus.body.color)}`); + const [red, green, blue] = this.deviceStatus.body.color!.split(':'); + this.platform.log.warn(`Color Bulb: ${this.accessory.displayName} red: ${JSON.stringify(red)}`); + this.platform.log.warn(`Color Bulb: ${this.accessory.displayName} green: ${JSON.stringify(green)}`); + this.platform.log.warn(`Color Bulb: ${this.accessory.displayName} blue: ${JSON.stringify(blue)}`); + + const [hue, saturation] = rgb2hs(Number(red), Number(green), Number(blue)); + this.platform.log.warn(`Color Bulb: ${this.accessory.displayName} hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`); + + // Hue + this.Hue = hue; + this.platform.log.warn(`Color Bulb: ${this.accessory.displayName} Hue: ${this.Hue}`); + + // Saturation + this.Saturation = saturation; + this.platform.log.warn(`Color Bulb: ${this.accessory.displayName} Saturation: ${this.Saturation}`); + } + + // ColorTemperature + if (this.deviceStatus.body.colorTemperature) { + // Convert mired to kelvin to nearest 100 (Govee seems to need this) + const mired = Math.round(1000000 / this.deviceStatus.body.colorTemperature); + + this.ColorTemperature = Number(mired); + this.platform.log.warn(`Color Bulb: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`); + } } async refreshStatus() { try { - this.platform.debug('Color Bulb - Reading', `${DeviceURL}/${this.device.deviceId}/status`); this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; - this.platform.device(`Color Bulb ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); + this.platform.device(`Color Bulb: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); this.parseStatus(); this.updateHomeKitCharacteristics(); } catch (e: any) { - this.platform.log.error(`Color Bulb ${this.accessory.displayName} failed to refresh status, Error Message ${JSON.stringify(e.message)}`); - this.platform.debug(`Color Bulb ${this.accessory.displayName}, Error: ${JSON.stringify(e)}`); + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } } /** * Pushes the requested changes to the SwitchBot API - * deviceType commandType Command command parameter Description - * Color Bulb - "command" "turnOff" "default" = set to OFF state - * Color Bulb - "command" "turnOn" "default" = set to ON state + * deviceType commandType Command command parameter Description + * Color Bulb - "command" "turnOff" "default" = set to OFF state + * Color Bulb - "command" "turnOn" "default" = set to ON state + * Color Bulb - "command" "toggle" "default" = toggle state + * Color Bulb - "command" "setBrightness" "{1-100}" = set brightness + * Color Bulb - "command" "setColor" "{0-255}:{0-255}:{0-255}" = set RGB color value + * Color Bulb - "command" "setColorTemperature" "{2700-6500}" = set color temperature */ async pushChanges() { - const payload = { - commandType: 'command', - parameter: 'default', - } as any; + try { + // Push On Update + if (this.On) { + const payload = { + commandType: 'command', + parameter: 'default', + } as payload; + + if (this.On) { + payload.command = 'turnOn'; + } else { + payload.command = 'turnOff'; + } - if (this.On) { - payload.command = 'turnOn'; - } else { - payload.command = 'turnOff'; - } + this.platform.log.info(`Color Bulb: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); + + // Make the API request + const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); + this.platform.debug(`Color Bulb: ${this.accessory.displayName} pushChanges: ${JSON.stringify(push.data)}`); + this.statusCode(push); + } + + // Push Brightness Update + if (this.On) { + const payload = { + commandType: 'command', + command: 'setBrightness', + parameter: `{${this.Brightness}}`, + } as payload; + + this.platform.log.info(`Color Bulb: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); + + // Make the API request + const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); + this.platform.debug(`Color Bulb: ${this.accessory.displayName} pushChanges: ${JSON.stringify(push.data)}`); + this.statusCode(push); + } + + // Push ColorTemperature Update + if (this.On) { + const kelvin = Math.round(1000000 / Number(this.ColorTemperature)); + + const payload = { + commandType: 'command', + command: 'setColorTemperature', + parameter: `{${kelvin}}`, + } as payload; + + this.platform.log.info(`Color Bulb: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); + + // Make the API request + const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); + this.platform.debug(`Color Bulb: ${this.accessory.displayName} pushChanges: ${JSON.stringify(push.data)}`); + this.statusCode(push); + } + + // Push Hue & Saturation Update + if (this.On) { + this.platform.log.warn(JSON.stringify(this.Hue)); + this.platform.log.warn(JSON.stringify(this.Saturation)); + + const [red, green, blue] = hs2rgb(Number(this.Hue), Number(this.Saturation)); + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} rgb: ${JSON.stringify([red, green, blue])}`); + + const payload = { + commandType: 'command', + command: 'setColor', + parameter: `{${red}}:{${green}}:{${blue}}`, + } as payload; + + this.platform.log.info(`Color Bulb: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); + + // Make the API request + const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); + this.platform.debug(`Color Bulb: ${this.accessory.displayName} pushChanges: ${JSON.stringify(push.data)}`); + this.statusCode(push); + } - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`Color Bulb ${this.accessory.displayName} pushchanges: ${JSON.stringify(payload)}`); - - // Make the API request - const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); - this.platform.debug(`Color Bulb ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`); - this.statusCode(push); + } catch (e: any) { + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + } } updateHomeKitCharacteristics() { if (this.On === undefined) { - this.platform.debug(`Color Bulb ${this.accessory.displayName} On: ${this.On}`); + this.platform.debug(`Color Bulb: ${this.accessory.displayName} On: ${this.On}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.On, this.On); - this.platform.device(`Color Bulb ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.platform.device(`Color Bulb: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); } if (this.Brightness === undefined) { - this.platform.debug(`Color Bulb ${this.accessory.displayName} Brightness: ${this.Brightness}`); + this.platform.debug(`Color Bulb: ${this.accessory.displayName} Brightness: ${this.Brightness}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.Brightness, this.Brightness); - this.platform.device(`Color Bulb ${this.accessory.displayName} updateCharacteristic Brightness: ${this.Brightness}`); + this.platform.device(`Color Bulb: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.Brightness}`); } if (this.ColorTemperature === undefined) { - this.platform.debug(`Color Bulb ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`); + this.platform.debug(`Color Bulb: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.Brightness, this.ColorTemperature); - this.platform.debug(`Color Bulb ${this.accessory.displayName} updateCharacteristic ColorTemperature: ${this.ColorTemperature}`); + this.platform.debug(`Color Bulb: ${this.accessory.displayName} updateCharacteristic ColorTemperature: ${this.ColorTemperature}`); + } + if (this.Hue === undefined) { + this.platform.debug(`Color Bulb: ${this.accessory.displayName} Hue: ${this.Hue}`); + } else { + this.service.updateCharacteristic(this.platform.Characteristic.Hue, this.Hue); + this.platform.debug(`Color Bulb: ${this.accessory.displayName} updateCharacteristic Hue: ${this.Hue}`); + } + if (this.Saturation === undefined) { + this.platform.debug(`Color Bulb: ${this.accessory.displayName} Saturation: ${this.Saturation}`); + } else { + this.service.updateCharacteristic(this.platform.Characteristic.Saturation, this.Saturation); + this.platform.debug(`Color Bulb: ${this.accessory.displayName} updateCharacteristic Saturation: ${this.Saturation}`); } } public apiError(e: any) { this.service.updateCharacteristic(this.platform.Characteristic.On, e); + this.service.updateCharacteristic(this.platform.Characteristic.Hue, e); this.service.updateCharacteristic(this.platform.Characteristic.Brightness, e); + this.service.updateCharacteristic(this.platform.Characteristic.Saturation, e); this.service.updateCharacteristic(this.platform.Characteristic.ColorTemperature, e); } - - private statusCode(push: AxiosResponse<{ statusCode: number;}>) { + private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`Color Bulb: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - if (this.platform.config.options?.debug) { - this.platform.log.info('Command successfully sent.'); - } + this.platform.debug(`Color Bulb: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`Color Bulb: ${this.accessory.displayName} Unknown statusCode.`); } } @@ -277,7 +421,7 @@ export class ColorBulb { * Handle requests to set the value of the "On" characteristic */ OnSet(value: CharacteristicValue) { - this.platform.debug(`${this.accessory.displayName} - Set On: ${value}`); + this.platform.debug(`Color Bulb: ${this.accessory.displayName} On: ${value}`); this.On = value; this.doColorBulbUpdate.next(); @@ -287,7 +431,7 @@ export class ColorBulb { * Handle requests to set the value of the "Brightness" characteristic */ BrightnessSet(value: CharacteristicValue) { - this.platform.debug(`${this.accessory.displayName} - Set On: ${value}`); + this.platform.debug(`Color Bulb: ${this.accessory.displayName} Brightness: ${value}`); this.Brightness = value; this.doColorBulbUpdate.next(); @@ -297,9 +441,30 @@ export class ColorBulb { * Handle requests to set the value of the "ColorTemperature" characteristic */ ColorTemperatureSet(value: CharacteristicValue) { - this.platform.debug(`${this.accessory.displayName} - Set On: ${value}`); + this.platform.debug(`Color Bulb: ${this.accessory.displayName} ColorTemperature: ${value}`); this.ColorTemperature = value; this.doColorBulbUpdate.next(); } + + /** + * Handle requests to set the value of the "Hue" characteristic + */ + HueSet(value: CharacteristicValue) { + this.platform.debug(`Color Bulb: ${this.accessory.displayName} Hue: ${value}`); + + this.Hue = value; + this.doColorBulbUpdate.next(); + } + + /** + * Handle requests to set the value of the "Saturation" characteristic + */ + SaturationSet(value: CharacteristicValue) { + this.platform.debug(`Color Bulb: ${this.accessory.displayName} Saturation: ${value}`); + + this.Saturation = value; + this.doColorBulbUpdate.next(); + } } + diff --git a/src/devices/contact.ts b/src/devices/contact.ts index 8efcbd1a..bb462497 100644 --- a/src/devices/contact.ts +++ b/src/devices/contact.ts @@ -34,6 +34,10 @@ export class Contact { doorState!: serviceData['doorState']; lightLevel!: serviceData['lightLevel']; + // Config + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; + // Updates contactUbpdateInProgress!: boolean; doContactUpdate!: Subject; @@ -43,20 +47,14 @@ export class Contact { private accessory: PlatformAccessory, public device: device & devicesConfig, ) { + // Contact Config + this.platform.device(`Contact Sensor: ${this.accessory.displayName} Config: (ble: ${device.ble},` + + ` hide_lightsensor: ${device.contact?.hide_lightsensor}, hide_motionsensor: ${device.contact?.hide_motionsensor})`); + + // default placeholders this.ContactSensorState = this.platform.Characteristic.ContactSensorState.CONTACT_DETECTED; - // BLE Connection - if (device.ble) { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const SwitchBot = require('node-switchbot'); - this.switchbot = new SwitchBot(); - const colon = device.deviceId!.match(/.{1,2}/g); - const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A; - this.device.bleMac = bleMac.toLowerCase(); - this.platform.device(this.device.bleMac.toLowerCase()); - } - // this is subject we use to track when we need to POST changes to the SwitchBot API this.doContactUpdate = new Subject(); this.contactUbpdateInProgress = false; @@ -90,11 +88,11 @@ export class Contact { // Motion Sensor Service if (this.device.contact?.hide_motionsensor) { - this.platform.device(`Contact: ${accessory.displayName} Removing Motion Sensor Service`); + this.platform.device(`Contact Sensor: ${accessory.displayName} Removing Motion Sensor Service`); this.motionService = this.accessory.getService(this.platform.Service.MotionSensor); accessory.removeService(this.motionService!); } else if (!this.motionService) { - this.platform.device(`Contact: ${accessory.displayName} Add Motion Sensor Service`); + this.platform.device(`Contact Sensor: ${accessory.displayName} Add Motion Sensor Service`); (this.motionService = this.accessory.getService(this.platform.Service.MotionSensor) || this.accessory.addService(this.platform.Service.MotionSensor)), `${accessory.displayName} Motion Sensor`; @@ -102,16 +100,16 @@ export class Contact { this.motionService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Motion Sensor`); } else { - this.platform.device(`Contact: ${accessory.displayName} Motion Sensor Service Not Added`); + this.platform.device(`Contact Sensor: ${accessory.displayName} Motion Sensor Service Not Added`); } // Light Sensor Service if (this.device.contact?.hide_lightsensor) { - this.platform.device(`Contact: ${accessory.displayName} Removing Light Sensor Service`); + this.platform.device(`Contact Sensor: ${accessory.displayName} Removing Light Sensor Service`); this.lightSensorService = this.accessory.getService(this.platform.Service.LightSensor); accessory.removeService(this.lightSensorService!); } else if (!this.lightSensorService) { - this.platform.device(`Contact: ${accessory.displayName} Add Light Sensor Service`); + this.platform.device(`Contact Sensor: ${accessory.displayName} Add Light Sensor Service`); (this.lightSensorService = this.accessory.getService(this.platform.Service.LightSensor) || this.accessory.addService(this.platform.Service.LightSensor)), `${accessory.displayName} Light Sensor`; @@ -119,7 +117,7 @@ export class Contact { this.lightSensorService.setCharacteristic(this.platform.Characteristic.Name, `${accessory.displayName} Light Sensor`); } else { - this.platform.device(`Contact: ${accessory.displayName} Light Sensor Service Not Added`); + this.platform.device(`Contact Sensor: ${accessory.displayName} Light Sensor Service Not Added`); } if (device.ble) { @@ -146,16 +144,14 @@ export class Contact { */ async parseStatus() { if (this.device.ble) { - this.platform.device('BLE'); await this.BLEparseStatus(); } else { - this.platform.device('OpenAPI'); await this.openAPIparseStatus(); } } private async BLEparseStatus() { - this.platform.debug('Contact BLE Device parseStatus'); + this.platform.debug(`Contact Sensor: ${this.accessory.displayName} BLE parseStatus`); if (this.device.contact?.hide_motionsensor) { // Movement this.MotionDetected = Boolean(this.movement); @@ -171,7 +167,7 @@ export class Contact { this.ContactSensorState = this.platform.Characteristic.ContactSensorState.CONTACT_DETECTED; break; default: - this.platform.log.error('timeout no closed'); + this.platform.log.error(`Contact Sensor: ${this.accessory.displayName} timeout no closed, doorstate: ${this.doorState}`); } if (this.device.contact?.hide_lightsensor) {// Light Level switch (this.lightLevel) { @@ -191,25 +187,27 @@ export class Contact { this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.platform.debug(`${this.accessory.displayName}, ContactSensorState: ${this.ContactSensorState}, MotionDetected: ${this.MotionDetected}` - + `CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}, BatteryLevel: ${this.BatteryLevel}`); + this.platform.debug(`Contact Sensor: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}, MotionDetected: ` + + `${this.MotionDetected}, CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}, BatteryLevel: ${this.BatteryLevel}`); } private async openAPIparseStatus() { - if (this.deviceStatus.body.openState === 'open') { - this.ContactSensorState = this.platform.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; - this.platform.log.info(`${this.accessory.displayName} ${this.deviceStatus.body.openState}`); - } else if (this.deviceStatus.body.openState === 'close') { - this.ContactSensorState = this.platform.Characteristic.ContactSensorState.CONTACT_DETECTED; - this.platform.device(`${this.accessory.displayName} ${this.deviceStatus.body.openState}`); - } else { - this.platform.device(`${this.accessory.displayName} ${this.deviceStatus.body.openState}`); - } - if (this.device.contact?.hide_motionsensor) { - this.MotionDetected = Boolean(this.deviceStatus.body.moveDetected); + if (this.platform.config.credentials?.openToken) { + this.platform.debug(`Contact Sensor: ${this.accessory.displayName} OpenAPI parseStatus`); + if (this.deviceStatus.body.openState === 'open') { + this.ContactSensorState = this.platform.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; + this.platform.device(`Contact Sensor: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`); + } else if (this.deviceStatus.body.openState === 'close') { + this.ContactSensorState = this.platform.Characteristic.ContactSensorState.CONTACT_DETECTED; + this.platform.device(`Contact Sensor: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`); + } else { + this.platform.device(`Contact Sensor: ${this.accessory.displayName} openState: ${this.deviceStatus.body.openState}`); + } + if (this.device.contact?.hide_motionsensor) { + this.MotionDetected = Boolean(this.deviceStatus.body.moveDetected); + } + this.platform.debug(`Contact Sensor: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`); } - this.platform.debug(`${this.accessory.displayName} - , ContactSensorState: ${this.ContactSensorState}, MotionDetected: ${this.MotionDetected}`); } /** @@ -217,10 +215,8 @@ export class Contact { */ async refreshStatus() { if (this.device.ble) { - this.platform.device('BLE'); await this.BLERefreshStatus(); } else { - this.platform.device('OpenAPI'); await this.openAPIRefreshStatus(); } } @@ -232,12 +228,12 @@ export class Contact { const colon = this.device.deviceId!.match(/.{1,2}/g); const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A; this.device.bleMac = bleMac.toLowerCase(); - this.platform.device(this.device.bleMac!); + this.platform.device(`Contact Sensor: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); return switchbot; } private async BLERefreshStatus() { - this.platform.debug('Contact BLE Device RefreshStatus'); + this.platform.debug(`Contact Sensor: ${this.accessory.displayName} BLE refreshStatus`); // eslint-disable-next-line @typescript-eslint/no-var-requires const switchbot = this.connectBLE(); // Start to monitor advertisement packets @@ -248,13 +244,13 @@ export class Contact { // Set an event hander switchbot.onadvertisement = (ad: any) => { this.serviceData = ad.serviceData; - this.platform.device(`${this.device.bleMac}: ${JSON.stringify(ad.serviceData)}`); this.movement = ad.serviceData.movement; this.doorState = ad.serviceData.doorState; this.lightLevel = ad.serviceData.lightLevel; this.battery = ad.serviceData.battery; - this.platform.device(`${this.accessory.displayName}, Movement: ${ad.serviceData.movement}, Door State: ${ad.serviceData.doorState},` - + ` Light Level: ${ad.serviceData.lightLevel}, Battery: ${ad.serviceData.battery}`); + this.platform.device(`Contact Sensor: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); + this.platform.device(`Contact Sensor: ${this.accessory.displayName} movement: ${ad.serviceData.movement}, doorState: ` + + `${ad.serviceData.doorState}, lightLevel: ${ad.serviceData.lightLevel}, battery: ${ad.serviceData.battery}`); }; // Wait 10 seconds return switchbot.wait(10000); @@ -264,22 +260,42 @@ export class Contact { this.parseStatus(); this.updateHomeKitCharacteristics(); }).catch(async (e: any) => { - this.platform.log.error(`BLE Connection Failed: ${e.message}`); - this.platform.log.warn('Using OpenAPI Connection'); - await this.openAPIRefreshStatus(); + this.platform.log.error(`Contact Sensor: ${this.accessory.displayName} failed refreshStatus with BLE Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Contact Sensor: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Contact Sensor: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + if (this.platform.config.credentials?.openToken) { + this.platform.log.warn(`Contact Sensor: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.openAPIRefreshStatus(); + } }); } private async openAPIRefreshStatus() { - try { - this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; - this.platform.debug(`Contact ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); - this.parseStatus(); - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.platform.log.error(`Contact ${this.accessory.displayName} failed to refresh status. Error Message: ${JSON.stringify(e.message)}`); - this.platform.debug(`Contact ${this.accessory.displayName}, Error: ${JSON.stringify(e)}`); - this.apiError(e); + if (this.platform.config.credentials?.openToken) { + this.platform.debug(`Contact Sensor: ${this.accessory.displayName} OpenAPI refreshStatus`); + try { + this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; + this.platform.debug(`Contact Sensor: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); + this.parseStatus(); + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.platform.log.error(`Contact Sensor: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Contact Sensor: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Contact Sensor: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + this.apiError(e); + } } } @@ -288,39 +304,40 @@ export class Contact { */ updateHomeKitCharacteristics() { if (this.ContactSensorState === undefined) { - this.platform.debug(`Contact ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`); + this.platform.debug(`Contact Sensor: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.ContactSensorState, this.ContactSensorState); - this.platform.device(`Contact ${this.accessory.displayName} updateCharacteristic ContactSensorState: ${this.ContactSensorState}`); + this.platform.device(`Contact Sensor: ${this.accessory.displayName} updateCharacteristic ContactSensorState: ${this.ContactSensorState}`); } if (!this.device.contact?.hide_motionsensor) { if (this.MotionDetected === undefined) { - this.platform.debug(`Contact ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`); + this.platform.debug(`Contact Sensor: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`); } else { this.motionService!.updateCharacteristic(this.platform.Characteristic.MotionDetected, this.MotionDetected); - this.platform.device(`Contact ${this.accessory.displayName} updateCharacteristic MotionDetected: ${this.MotionDetected}`); + this.platform.device(`Contact Sensor: ${this.accessory.displayName} updateCharacteristic MotionDetected: ${this.MotionDetected}`); } } if (!this.device.contact?.hide_lightsensor) { if (this.CurrentAmbientLightLevel === undefined) { - this.platform.debug(`Contact ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + this.platform.debug(`Contact Sensor: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); } else { this.lightSensorService!.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel); - this.platform.device(`Contact ${this.accessory.displayName} updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + this.platform.device(`Contact Sensor: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); } } if (this.device.ble) { if (this.BatteryLevel === undefined) { - this.platform.debug(`Contact ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + this.platform.debug(`Contact Sensor: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); } else { this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel); - this.platform.device(`Contact ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); + this.platform.device(`Contact Sensor: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); } if (this.StatusLowBattery === undefined) { - this.platform.debug(`Contact ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + this.platform.debug(`Contact Sensor: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); } else { this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.platform.device(`Contact ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); + this.platform.device(`Contact Sensor: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); } } } diff --git a/src/devices/curtains.ts b/src/devices/curtains.ts index b21907d0..dcbe5390 100644 --- a/src/devices/curtains.ts +++ b/src/devices/curtains.ts @@ -2,7 +2,7 @@ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; import { interval, Subject } from 'rxjs'; import { debounceTime, skipWhile, tap } from 'rxjs/operators'; -import { DeviceURL, device, devicesConfig, serviceData, switchbot, deviceStatusResponse } from '../settings'; +import { DeviceURL, device, devicesConfig, serviceData, switchbot, deviceStatusResponse, payload } from '../settings'; import { AxiosResponse } from 'axios'; export class Curtain { @@ -25,6 +25,9 @@ export class Curtain { setNewTargetTimer!: NodeJS.Timeout; // BLE Others + set_minLux!: number; + set_maxLux!: number; + spaceBetweenLevels!: number; switchbot!: switchbot; serviceData!: serviceData; calibration: serviceData['calibration']; @@ -35,6 +38,8 @@ export class Curtain { // Config set_minStep!: number; refreshRate!: number; + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; // Updates curtainUpdateInProgress!: boolean; @@ -46,9 +51,10 @@ export class Curtain { public device: device & devicesConfig, ) { // Curtain Config - this.platform.device(`[Curtain Config] ble: ${device.ble}, disable_group: ${device.curtain?.disable_group},` - + ` refreshRate: ${device.curtain?.refreshRate}, set_max: ${device.curtain?.set_max}, set_min: ${device.curtain?.set_min},` - + ` set_minStep: ${device.curtain?.set_minStep}`); + this.platform.device(`Curtain: ${this.accessory.displayName} Config: (ble: ${device.ble}, disable_group: ${device.curtain?.disable_group},` + + ` hide_lightsensor: ${device.curtain?.hide_lightsensor}, set_minLux: ${device.curtain?.set_minLux}, set_maxLux: ` + + `${device.curtain?.set_maxLux}, refreshRate: ${device.curtain?.refreshRate}, set_max: ${device.curtain?.set_max}, set_min: ` + + `${device.curtain?.set_min}, set_minStep: ${device.curtain?.set_minStep})`); // default placeholders this.setMinMax(); @@ -164,8 +170,15 @@ export class Curtain { try { await this.pushChanges(); } catch (e: any) { - this.platform.log.error(`Curtain: ${this.accessory.displayName} Error Message ${JSON.stringify(e.message)}`); - this.platform.debug(`Curtain: ${accessory.displayName} Error: ${JSON.stringify(e)}`); + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed pushChanges`); + if (this.deviceDebug) { + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed pushChanges,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed pushChanges,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } this.curtainUpdateInProgress = false; @@ -179,7 +192,7 @@ export class Curtain { const colon = this.device.deviceId!.match(/.{1,2}/g); const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A; this.device.bleMac = bleMac.toLowerCase(); - this.platform.device(this.device.bleMac.toLowerCase()); + this.platform.device(`Curtain: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); return switchbot; } @@ -189,13 +202,13 @@ export class Curtain { } else { this.refreshRate = 5; if (this.platform.config.options?.debug === 'device') { - this.platform.log.warn('Using Default Curtain Refresh Rate.'); + this.platform.log.warn(`Curtain: ${this.accessory.displayName} Using Default Curtain Refresh Rate.`); } } return this.refreshRate; } - private minStep(): number | undefined { + private minStep(): number { if (this.device.curtain?.set_minStep) { this.set_minStep = this.device.curtain?.set_minStep; } else { @@ -204,82 +217,114 @@ export class Curtain { return this.set_minStep; } + private minLux(): number { + if (this.device.curtain?.set_minLux) { + this.set_minLux = this.device.curtain?.set_minLux; + } else { + this.set_minLux = 1; + } + return this.set_minLux; + } + + private maxLux(): number { + if (this.device.curtain?.set_maxLux) { + this.set_maxLux = this.device.curtain?.set_maxLux; + } else { + this.set_maxLux = 6001; + } + return this.set_maxLux; + } + async parseStatus() { if (this.device.ble) { - this.platform.device('BLE'); await this.BLEparseStatus(); } else { - this.platform.device('OpenAPI'); await this.openAPIparseStatus(); } } private async BLEparseStatus() { - this.platform.device('Curtains BLE Device parseStatus'); + this.platform.debug(`Curtain: ${this.accessory.displayName} BLE parseStatus`); this.CurrentPosition = 100 - Number(this.position); - this.platform.debug(`Curtain ${this.accessory.displayName} CurrentPosition ${this.CurrentPosition}`); + this.platform.debug(`Curtain: ${this.accessory.displayName} CurrentPosition ${this.CurrentPosition}`); if (this.setNewTarget) { - this.platform.log.info(`Checking ${this.accessory.displayName} Status ...`); + this.platform.log.info(`Curtain: ${this.accessory.displayName} Checking Status ...`); } if (this.setNewTarget) { this.setMinMax(); if (this.TargetPosition > this.CurrentPosition) { - this.platform.debug(`Curtain ${this.accessory.displayName} Closing, Current position: ${this.CurrentPosition}`); + this.platform.debug(`Curtain: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition}`); this.PositionState = this.platform.Characteristic.PositionState.INCREASING; } else if (this.TargetPosition < this.CurrentPosition) { - this.platform.debug(`Curtain ${this.accessory.displayName} Opening, Current position: ${this.CurrentPosition}`); + this.platform.debug(`Curtain: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition}`); this.PositionState = this.platform.Characteristic.PositionState.DECREASING; } else { - this.platform.debug(`Curtain ${this.CurrentPosition} Standby, Current position: ${this.CurrentPosition}`); + this.platform.debug(`Curtain: ${this.CurrentPosition} Standby, CurrentPosition: ${this.CurrentPosition}`); this.PositionState = this.platform.Characteristic.PositionState.STOPPED; } } else { - this.platform.debug(`Curtain ${this.accessory.displayName} Standby, Current position: ${this.CurrentPosition}`); + this.platform.debug(`Curtain: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`); if (!this.setNewTarget) { // If Curtain calibration distance is short, there will be an error between the current percentage and the target percentage. this.TargetPosition = this.CurrentPosition; this.PositionState = this.platform.Characteristic.PositionState.STOPPED; } } - this.platform.debug(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` + this.platform.debug(`Curtain: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` + ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`); if (!this.device.curtain?.hide_lightsensor) { + this.set_minLux = this.minLux(); + this.set_maxLux = this.maxLux(); + this.spaceBetweenLevels = 9; + // Brightness switch (this.lightLevel) { case 1: - this.CurrentAmbientLightLevel = 1; + this.CurrentAmbientLightLevel = this.set_minLux; + this.platform.device(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`); break; case 2: - this.CurrentAmbientLightLevel = 2; + this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels); + this.platform.device(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel},` + + ` Calculation: ${((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels)}`); break; case 3: - this.CurrentAmbientLightLevel = 3; + this.CurrentAmbientLightLevel = (((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 2); + this.platform.device(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`); break; case 4: - this.CurrentAmbientLightLevel = 4; + this.CurrentAmbientLightLevel = (((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 3); + this.platform.device(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`); break; case 5: - this.CurrentAmbientLightLevel = 5; + this.CurrentAmbientLightLevel = (((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 4); + this.platform.device(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`); break; case 6: - this.CurrentAmbientLightLevel = 6; + this.CurrentAmbientLightLevel = (((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 5); + this.platform.device(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`); break; case 7: - this.CurrentAmbientLightLevel = 7; + this.CurrentAmbientLightLevel = (((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 6); + this.platform.device(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`); break; case 8: - this.CurrentAmbientLightLevel = 8; + this.CurrentAmbientLightLevel = (((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 7); + this.platform.device(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`); break; case 9: - this.CurrentAmbientLightLevel = 9; + this.CurrentAmbientLightLevel = (((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 8); + this.platform.device(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel}`); break; case 10: default: - this.CurrentAmbientLightLevel = 10; + this.CurrentAmbientLightLevel = this.set_maxLux; + this.platform.device(); } - this.platform.debug(`Curtain ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + this.platform.debug(`Curtain: ${this.accessory.displayName} LightLevel: ${this.lightLevel},` + + ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); } // Battery this.BatteryLevel = Number(this.battery); @@ -288,67 +333,67 @@ export class Curtain { } else { this.StatusLowBattery = this.platform.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL; } - this.platform.debug(`Curtain ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); + this.platform.debug(`Curtain: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); } private async openAPIparseStatus() { - this.platform.device('Curtains OpenAPI Device parseStatus'); - // CurrentPosition - this.CurrentPosition = 100 - this.deviceStatus.body.slidePosition!; - this.platform.debug(`Curtain ${this.accessory.displayName} CurrentPosition - Device is Currently: ${this.CurrentPosition}`); - if (this.setNewTarget) { - this.platform.log.info(`Checking ${this.accessory.displayName} Status ...`); - } + if (this.platform.config.credentials?.openToken) { + this.platform.debug(`Curtain: ${this.accessory.displayName} OpenAPI parseStatus`); + // CurrentPosition + this.CurrentPosition = 100 - this.deviceStatus.body.slidePosition!; + this.platform.debug(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`); + if (this.setNewTarget) { + this.platform.log.info(`Checking ${this.accessory.displayName} Status ...`); + } - if (this.deviceStatus.body.moving) { - this.setMinMax(); - if (this.TargetPosition > this.CurrentPosition) { - this.platform.debug(`Curtain: ${this.accessory.displayName} Closing, Current Position: ${this.CurrentPosition} `); - this.PositionState = this.platform.Characteristic.PositionState.INCREASING; - } else if (this.TargetPosition < this.CurrentPosition) { - this.platform.debug(`Curtain: ${this.accessory.displayName} Opening, Current Position: ${this.CurrentPosition} `); - this.PositionState = this.platform.Characteristic.PositionState.DECREASING; + if (this.deviceStatus.body.moving) { + this.setMinMax(); + if (this.TargetPosition > this.CurrentPosition) { + this.platform.debug(`Curtain: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition} `); + this.PositionState = this.platform.Characteristic.PositionState.INCREASING; + } else if (this.TargetPosition < this.CurrentPosition) { + this.platform.debug(`Curtain: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition} `); + this.PositionState = this.platform.Characteristic.PositionState.DECREASING; + } else { + this.platform.debug(`Curtain: ${this.CurrentPosition} Standby, CurrentPosition: ${this.CurrentPosition}`); + this.PositionState = this.platform.Characteristic.PositionState.STOPPED; + } } else { - this.platform.debug(`Curtain: ${this.CurrentPosition} Standby, Current position: ${this.CurrentPosition}`); - this.PositionState = this.platform.Characteristic.PositionState.STOPPED; - } - } else { - this.platform.debug(`Curtain: ${this.accessory.displayName} Standby, Current position: ${this.CurrentPosition}`); - if (!this.setNewTarget) { - /*If Curtain calibration distance is short, there will be an error between the current percentage and the target percentage.*/ - this.TargetPosition = this.CurrentPosition; - this.PositionState = this.platform.Characteristic.PositionState.STOPPED; + this.platform.debug(`Curtain: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`); + if (!this.setNewTarget) { + /*If Curtain calibration distance is short, there will be an error between the current percentage and the target percentage.*/ + this.TargetPosition = this.CurrentPosition; + this.PositionState = this.platform.Characteristic.PositionState.STOPPED; + } } - } - this.platform.debug(`Curtain: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` - + ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`); - - if (!this.device.curtain?.hide_lightsensor) { - // Brightness - switch (this.deviceStatus.body.brightness) { - case 'dim': - this.CurrentAmbientLightLevel = 0.0001; - break; - case 'bright': - default: - this.CurrentAmbientLightLevel = 100000; + this.platform.debug(`Curtain: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` + + ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`); + + if (!this.device.curtain?.hide_lightsensor) { + // Brightness + switch (this.deviceStatus.body.brightness) { + case 'dim': + this.CurrentAmbientLightLevel = 0.0001; + break; + case 'bright': + default: + this.CurrentAmbientLightLevel = 100000; + } + this.platform.debug(`Curtain: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); } - this.platform.debug(`Curtain: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); } } async refreshStatus() { if (this.device.ble) { - this.platform.device('BLE'); await this.BLERefreshStatus(); } else { - this.platform.device('OpenAPI'); await this.openAPIRefreshStatus(); } } private async BLERefreshStatus() { - this.platform.debug('Curtains BLE Device RefreshStatus'); + this.platform.debug(`Curtain: ${this.accessory.displayName} BLE refreshStatus`); // eslint-disable-next-line @typescript-eslint/no-var-requires const switchbot = this.connectBLE(); // Start to monitor advertisement packets @@ -359,13 +404,13 @@ export class Curtain { // Set an event hander switchbot.onadvertisement = (ad: any) => { this.serviceData = ad.serviceData; - this.platform.device(`${this.device.bleMac}: ${JSON.stringify(ad.serviceData)}`); this.calibration = ad.serviceData.calibration; this.battery = ad.serviceData.battery; this.position = ad.serviceData.position; this.lightLevel = ad.serviceData.lightLevel; - this.platform.device(`${this.accessory.displayName}, Calibration: ${ad.serviceData.calibration}, Position: ${ad.serviceData.position},` - + ` Light Level: ${ad.serviceData.lightLevel}, Battery: ${ad.serviceData.battery}`); + this.platform.device(`Curtain: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); + this.platform.device(`Curtain: ${this.accessory.displayName} calibration: ${ad.serviceData.calibration}, ` + + `position: ${ad.serviceData.position}, lightLevel: ${ad.serviceData.lightLevel}, battery: ${ad.serviceData.battery}`); }; // Wait 10 seconds return switchbot.wait(10000); @@ -375,75 +420,111 @@ export class Curtain { this.parseStatus(); this.updateHomeKitCharacteristics(); }).catch(async (e: any) => { - this.platform.log.error(`BLE Connection Failed: ${e.message}`); - this.platform.log.warn('Using OpenAPI Connection'); - await this.openAPIRefreshStatus(); + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed refreshStatus with BLE Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + if (this.platform.config.credentials?.openToken) { + this.platform.log.warn(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.openAPIRefreshStatus(); + } }); } private async openAPIRefreshStatus() { - this.platform.debug('Curtains OpenAPI Device RefreshStatus'); - try { - this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; - this.platform.debug(`Curtain: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); - this.parseStatus(); - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.platform.log.error(`Curtain: ${this.accessory.displayName} failed to refresh status, Error Message ${JSON.stringify(e.message)}`); - this.platform.debug(`Curtain: ${this.accessory.displayName} Error: ${JSON.stringify(e)}`); - this.apiError(e); + if (this.platform.config.credentials?.openToken) { + this.platform.debug(`Curtain: ${this.accessory.displayName} OpenAPI refreshStatus`); + try { + this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; + this.platform.debug(`Curtain: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); + this.parseStatus(); + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + this.apiError(e); + } } } async pushChanges() { if (this.device.ble) { - this.platform.device('BLE'); await this.BLEpushChanges(); } else { - this.platform.device('OpenAPI'); await this.OpenAPIpushChanges(); } + this.refreshStatus(); } private async BLEpushChanges() { - this.platform.device('Curtains BLE Device pushChanges'); + this.platform.debug(`Curtain: ${this.accessory.displayName} BLE pushChanges`); const switchbot = this.connectBLE(); switchbot.discover({ model: 'c', quick: true, id: this.device.bleMac }).then((device_list) => { this.platform.log.info(`${this.accessory.displayName} Target Position: ${this.TargetPosition}`); return device_list[0].runToPos(100 - Number(this.TargetPosition)); }).then(() => { - this.platform.device('Done.'); - }).catch((e: any) => { - this.platform.log.error(`BLE pushChanges Error Message: ${e.message}`); + this.platform.device(`Curtain: ${this.accessory.displayName} Done.`); + }).catch(async (e: any) => { + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed pushChanges with BLE Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + if (this.platform.config.credentials?.openToken) { + this.platform.log.warn(`Curtain: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.OpenAPIpushChanges(); + } }); } private async OpenAPIpushChanges() { - if (this.TargetPosition !== this.CurrentPosition) { - this.platform.debug(`Pushing ${this.TargetPosition}`); - const adjustedTargetPosition = 100 - Number(this.TargetPosition); - const payload = { - commandType: 'command', - command: 'setPosition', - parameter: `0,ff,${adjustedTargetPosition}`, - } as any; - - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`Curtain: ${this.accessory.displayName} pushchanges: ${JSON.stringify(payload)}`); - - // Make the API request - const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId!}/commands`, payload)); - this.platform.debug(`Curtain: ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`); - this.statusCode(push); + if (this.platform.config.credentials?.openToken) { + try { + this.platform.debug(`Curtain: ${this.accessory.displayName} OpenAPI pushChanges`); + if (this.TargetPosition !== this.CurrentPosition) { + this.platform.debug(`Pushing ${this.TargetPosition}`); + const adjustedTargetPosition = 100 - Number(this.TargetPosition); + const payload = { + commandType: 'command', + command: 'setPosition', + parameter: `0,ff,${adjustedTargetPosition}`, + } as payload; + + this.platform.log.info(`Curtain: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); + + // Make the API request + const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId!}/commands`, payload)); + this.platform.debug(`Curtain: ${this.accessory.displayName} pushchanges: ${JSON.stringify(push.data)}`); + this.statusCode(push); + } + } catch (e: any) { + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Curtain: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + } } } @@ -477,16 +558,16 @@ export class Curtain { } if (this.device.ble) { if (this.BatteryLevel === undefined) { - this.platform.debug(`Curtain ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + this.platform.debug(`Curtain: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); } else { this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel); - this.platform.device(`Curtain ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); + this.platform.device(`Curtain: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); } if (this.StatusLowBattery === undefined) { - this.platform.debug(`Curtain ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + this.platform.debug(`Curtain: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); } else { this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.platform.device(`Curtain ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); + this.platform.device(`Curtain: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); } } } @@ -507,28 +588,29 @@ export class Curtain { private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`Curtain: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`Curtain: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`Curtain: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`Curtain: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`Curtain: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`Curtain: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - this.platform.debug('Command successfully sent.'); + this.platform.debug(`Curtain: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`Curtain: ${this.accessory.displayName} Unknown statusCode.`); } } @@ -536,7 +618,7 @@ export class Curtain { * Handle requests to set the value of the "Target Position" characteristic */ TargetPositionSet(value: CharacteristicValue) { - this.platform.debug(`Curtain: ${this.accessory.displayName} - Set TargetPosition: ${value}`); + this.platform.debug(`Curtain: ${this.accessory.displayName} TargetPosition: ${value}`); this.TargetPosition = value; @@ -563,7 +645,7 @@ export class Curtain { clearTimeout(this.setNewTargetTimer); if (this.setNewTarget) { this.setNewTargetTimer = setTimeout(() => { - this.platform.debug(`Curtain: ${this.accessory.displayName} - setNewTarget ${this.setNewTarget} timeout`); + this.platform.debug(`Curtain: ${this.accessory.displayName} setNewTarget ${this.setNewTarget} timeout`); this.setNewTarget = false; }, 10000); } diff --git a/src/devices/humidifiers.ts b/src/devices/humidifiers.ts index 2e9363cb..d1e6160f 100644 --- a/src/devices/humidifiers.ts +++ b/src/devices/humidifiers.ts @@ -2,7 +2,7 @@ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; import { interval, Subject } from 'rxjs'; import { debounceTime, skipWhile, tap } from 'rxjs/operators'; -import { DeviceURL, device, devicesConfig, serviceData, ad, deviceStatusResponse } from '../settings'; +import { DeviceURL, device, devicesConfig, serviceData, ad, deviceStatusResponse, payload } from '../settings'; import { AxiosResponse } from 'axios'; /** @@ -35,6 +35,8 @@ export class Humidifier { // Config set_minStep?: number; + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; // Updates humidifierUpdateInProgress!: boolean; @@ -46,8 +48,8 @@ export class Humidifier { public device: device & devicesConfig, ) { // Humidifiers Config - this.platform.device(`[Humidifiers Config] ble: ${device.ble}, hide_temperature: ${device.humidifier?.hide_temperature},` - + ` set_minStep: ${device.humidifier?.set_minStep}`); + this.platform.device(`Humidifier: ${this.accessory.displayName} Config: (ble: ${device.ble}, hide_temperature: ` + + `${device.humidifier?.hide_temperature}, set_minStep: ${device.humidifier?.set_minStep})`); // default placeholders this.CurrentRelativeHumidity = 0; @@ -119,11 +121,11 @@ export class Humidifier { // create a new Temperature Sensor service // Temperature Sensor Service - if (device.humidifier?.hide_temperature) { + if (device.humidifier?.hide_temperature || device.ble) { this.platform.device(`Humidifier: ${accessory.displayName} Removing Temperature Sensor Service`); this.temperatureservice = this.accessory.getService(this.platform.Service.TemperatureSensor); accessory.removeService(this.temperatureservice!); - } else if (!this.temperatureservice) { + } else if (!this.temperatureservice && !device.ble) { this.platform.device(`Humidifier: ${accessory.displayName} Add Temperature Sensor Service`); (this.temperatureservice = this.accessory.getService(this.platform.Service.TemperatureSensor) || @@ -171,8 +173,15 @@ export class Humidifier { try { await this.pushChanges(); } catch (e: any) { - this.platform.log.error(JSON.stringify(e.message)); - this.platform.debug(`Humidifier ${accessory.displayName} - ${JSON.stringify(e)}`); + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushChanges`); + if (this.deviceDebug) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushChanges,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushChanges,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } this.humidifierUpdateInProgress = false; @@ -192,25 +201,65 @@ export class Humidifier { * Parse the device status from the SwitchBot api */ parseStatus() { - // Current Relative Humidity if (this.device.ble) { - this.CurrentRelativeHumidity = this.percentage!; - } else { - this.CurrentRelativeHumidity = this.deviceStatus.body.humidity!; - } - this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); - this.platform.device(JSON.stringify(this.deviceStatus.body)); - // Water Level - if (this.deviceStatus.body.lackWater) { - this.WaterLevel = 0; + this.BLEparseStatus(); } else { - this.WaterLevel = 100; + this.openAPIparseStatus(); } - this.platform.debug(`Humidifier ${this.accessory.displayName} WaterLevel: ${this.WaterLevel}`); + } + + private BLEparseStatus() { + this.platform.debug(`Humidifier: ${this.accessory.displayName} BLE parseStatus`); + // Current Relative Humidity + this.CurrentRelativeHumidity = this.percentage!; + this.platform.debug(`Humidifier: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); // Active - if (this.device.ble) { - this.Active = this.State(); + if (this.onState) { + this.Active = this.platform.Characteristic.Active.ACTIVE; } else { + this.Active = this.platform.Characteristic.Active.INACTIVE; + } + this.platform.debug(`Humidifier: ${this.accessory.displayName} Active: ${this.Active}`); + } + + private openAPIparseStatus() { + if (this.platform.config.credentials?.openToken) { + this.platform.debug(`Humidifier: ${this.accessory.displayName} OpenAPI parseStatus`); + // Current Relative Humidity + this.CurrentRelativeHumidity = this.deviceStatus.body.humidity!; + this.platform.debug(`Humidifier: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + // Current Temperature + if (!this.device.humidifier?.hide_temperature) { + this.CurrentTemperature = Number(this.deviceStatus.body.temperature); + this.platform.debug(`Humidifier: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); + } + // Target Humidifier Dehumidifier State + switch (this.deviceStatus.body.auto) { + case true: + this.TargetHumidifierDehumidifierState = this.platform.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER; + this.CurrentHumidifierDehumidifierState = this.platform.Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING; + this.RelativeHumidityHumidifierThreshold = this.CurrentRelativeHumidity; + break; + default: + this.TargetHumidifierDehumidifierState = this.platform.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER; + if (this.deviceStatus.body.nebulizationEfficiency! > 100) { + this.RelativeHumidityHumidifierThreshold = 100; + } else { + this.RelativeHumidityHumidifierThreshold = this.deviceStatus.body.nebulizationEfficiency!; + } + if (this.CurrentRelativeHumidity > this.RelativeHumidityHumidifierThreshold) { + this.CurrentHumidifierDehumidifierState = this.platform.Characteristic.CurrentHumidifierDehumidifierState.IDLE; + } else if (this.Active === this.platform.Characteristic.Active.INACTIVE) { + this.CurrentHumidifierDehumidifierState = this.platform.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE; + } else { + this.CurrentHumidifierDehumidifierState = this.platform.Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING; + } + } + this.platform.debug(`Humidifier: ${this.accessory.displayName} TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}`); + this.platform.debug(`Humidifier: ${this.accessory.displayName}` + + ` RelativeHumidityHumidifierThreshold: ${this.RelativeHumidityHumidifierThreshold}`); + this.platform.debug(`Humidifier: ${this.accessory.displayName} CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`); + // Active switch (this.deviceStatus.body.power) { case 'on': this.Active = this.platform.Characteristic.Active.ACTIVE; @@ -218,45 +267,14 @@ export class Humidifier { default: this.Active = this.platform.Characteristic.Active.INACTIVE; } - } - this.platform.debug(`Humidifier ${this.accessory.displayName} Active: ${this.Active}`); - // Target Humidifier Dehumidifier State - switch (this.deviceStatus.body.auto) { - case true: - this.TargetHumidifierDehumidifierState = this.platform.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER; - this.CurrentHumidifierDehumidifierState = this.platform.Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING; - this.RelativeHumidityHumidifierThreshold = this.CurrentRelativeHumidity; - break; - default: - this.TargetHumidifierDehumidifierState = this.platform.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER; - if (this.deviceStatus.body.nebulizationEfficiency! > 100) { - this.RelativeHumidityHumidifierThreshold = 100; - } else { - this.RelativeHumidityHumidifierThreshold = this.deviceStatus.body.nebulizationEfficiency!; - } - if (this.CurrentRelativeHumidity > this.RelativeHumidityHumidifierThreshold) { - this.CurrentHumidifierDehumidifierState = this.platform.Characteristic.CurrentHumidifierDehumidifierState.IDLE; - } else if (this.Active === this.platform.Characteristic.Active.INACTIVE) { - this.CurrentHumidifierDehumidifierState = this.platform.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE; - } else { - this.CurrentHumidifierDehumidifierState = this.platform.Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING; - } - } - this.platform.debug(`Humidifier ${this.accessory.displayName} TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}`); - this.platform.debug(`Humidifier ${this.accessory.displayName} RelativeHumidityHumidifierThreshold: ${this.RelativeHumidityHumidifierThreshold}`); - this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`); - // Current Temperature - if (!this.device.humidifier?.hide_temperature) { - this.CurrentTemperature = Number(this.deviceStatus.body.temperature); - this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); - } - } - - private State(): CharacteristicValue { - if (this.onState) { - return this.platform.Characteristic.Active.ACTIVE; - } else { - return this.platform.Characteristic.Active.INACTIVE; + this.platform.debug(`Humidifier: ${this.accessory.displayName} Active: ${this.Active}`); + // Water Level + if (this.deviceStatus.body.lackWater) { + this.WaterLevel = 0; + } else { + this.WaterLevel = 100; + } + this.platform.debug(`Humidifier: ${this.accessory.displayName} WaterLevel: ${this.WaterLevel}`); } } @@ -265,10 +283,8 @@ export class Humidifier { */ async refreshStatus() { if (this.device.ble) { - this.platform.device('BLE'); await this.BLERefreshStatus(); } else { - this.platform.device('OpenAPI'); await this.openAPIRefreshStatus(); } } @@ -280,12 +296,12 @@ export class Humidifier { const colon = this.device.deviceId!.match(/.{1,2}/g); const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A; this.device.bleMac = bleMac.toLowerCase(); - this.platform.device(this.device.bleMac!); + this.platform.device(`Humidifier: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); return switchbot; } private async BLERefreshStatus() { - this.platform.device('Bot BLE Device refreshStatus'); + this.platform.device(`Humidifier: ${this.accessory.displayName} BLE refreshStatus`); // eslint-disable-next-line @typescript-eslint/no-var-requires const switchbot = this.connectBLE(); // Start to monitor advertisement packets @@ -299,8 +315,8 @@ export class Humidifier { this.autoMode = ad.serviceData.autoMode; this.onState = ad.serviceData.onState; this.percentage = ad.serviceData.percentage; - this.platform.device(`${this.device.bleMac}: ${JSON.stringify(ad.serviceData)}`); - this.platform.device(`${this.accessory.displayName}, Model: ${ad.serviceData.model}, Model Name: ${ad.serviceData.modelName},` + this.platform.device(`Humidifier: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); + this.platform.device(`Humidifier: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName},` + `autoMode: ${ad.serviceData.autoMode}, onState: ${ad.serviceData.onState}, percentage: ${ad.serviceData.percentage}`); }; // Wait 10 seconds @@ -311,26 +327,46 @@ export class Humidifier { this.parseStatus(); this.updateHomeKitCharacteristics(); }).catch(async (e: any) => { - this.platform.log.error(`BLE Connection Failed: ${e.message}`); - this.platform.log.warn('Using OpenAPI Connection'); - await this.openAPIRefreshStatus(); + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed refreshStatus with BLE Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + if (this.platform.config.credentials?.openToken) { + this.platform.log.warn(`Humidifier: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.openAPIRefreshStatus(); + } }); } private async openAPIRefreshStatus() { - try { - this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; - if (this.deviceStatus.message === 'success') { - this.platform.debug(`Humidifier ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); - this.parseStatus(); - this.updateHomeKitCharacteristics(); - } else { - this.platform.debug(this.deviceStatus); + if (this.platform.config.credentials?.openToken) { + this.platform.device(`Humidifier: ${this.accessory.displayName} OpenAPI refreshStatus`); + try { + this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; + if (this.deviceStatus.message === 'success') { + this.platform.debug(`Humidifier: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); + this.parseStatus(); + this.updateHomeKitCharacteristics(); + } else { + this.platform.debug(this.deviceStatus); + } + } catch (e: any) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + this.apiError(e); } - } catch (e: any) { - this.platform.log.error(`Humidifier ${this.accessory.displayName}, Failed to update status. Error Message: ${JSON.stringify(e.message)}`); - this.platform.debug(`Humidifier ${this.accessory.displayName}, Error: ${JSON.stringify(e)}`); - this.apiError(e); } } @@ -338,42 +374,83 @@ export class Humidifier { * Pushes the requested changes to the SwitchBot API */ async pushChanges() { - if ( - this.TargetHumidifierDehumidifierState === - this.platform.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER && - this.Active === this.platform.Characteristic.Active.ACTIVE - ) { - this.platform.debug(`Pushing Manual: ${this.RelativeHumidityHumidifierThreshold}!`); - const payload = { - commandType: 'command', - command: 'setMode', - parameter: `${this.RelativeHumidityHumidifierThreshold}`, - } as any; - - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`Humidifier ${this.accessory.displayName} pushChanges: ${JSON.stringify(payload)}`); - - // Make the API request - const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); - this.platform.debug(`Humidifier ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`); - this.statusCode(push); - } else if ( - this.TargetHumidifierDehumidifierState === - this.platform.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER && - this.Active === this.platform.Characteristic.Active.ACTIVE - ) { - await this.pushAutoChanges(); - } else { - await this.pushActiveChanges(); + //if (this.device.ble) { + // await this.BLEpushChanges(); + //} else { + await this.OpenAPIpushChanges(); + //} + this.refreshStatus(); + } + + private async BLEpushChanges() { + this.platform.debug(`Humidifier: ${this.accessory.displayName} BLE pushChanges`); + const switchbot = this.connectBLE(); + switchbot.discover({ model: 'e', quick: true, id: this.device.bleMac }).then((device_list) => { + this.platform.log.info(`${this.accessory.displayName} Target Position: ${this.Active}`); + return device_list[0].runToPos(100 - Number(this.Active)); + }).then(() => { + this.platform.device(`Humidifier: ${this.accessory.displayName} Done.`); + }).catch(async (e: any) => { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushChanges with BLE Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushChanges with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + if (this.platform.config.credentials?.openToken) { + this.platform.log.warn(`Humidifier: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.OpenAPIpushChanges(); + } + }); + } + + private async OpenAPIpushChanges() { + if (this.platform.config.credentials?.openToken) { + this.platform.device(`Humidifier: ${this.accessory.displayName} OpenAPI pushChanges`); + if ( + this.TargetHumidifierDehumidifierState === + this.platform.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER && + this.Active === this.platform.Characteristic.Active.ACTIVE + ) { + try { + this.platform.debug(`Pushing Manual: ${this.RelativeHumidityHumidifierThreshold}!`); + const payload = { + commandType: 'command', + command: 'setMode', + parameter: `${this.RelativeHumidityHumidifierThreshold}`, + } as payload; + + this.platform.log.info(`Humidifier: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); + + // Make the API request + const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); + this.platform.debug(`Humidifier: ${this.accessory.displayName} pushChanges: ${JSON.stringify(push.data)}`); + this.statusCode(push); + } catch (e: any) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + this.apiError(e); + } + } else if ( + this.TargetHumidifierDehumidifierState === + this.platform.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER && + this.Active === this.platform.Characteristic.Active.ACTIVE + ) { + await this.pushAutoChanges(); + } else { + await this.pushActiveChanges(); + } } } @@ -392,28 +469,26 @@ export class Humidifier { commandType: 'command', command: 'setMode', parameter: 'auto', - } as any; - - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`Humidifier ${this.accessory.displayName} pushAutoChanges: ${JSON.stringify(payload)}`); + } as payload; + + this.platform.log.info(`Humidifier: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); // Make the API request const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); - this.platform.debug(`Humidifier ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`); + this.platform.debug(`Humidifier: ${this.accessory.displayName} pushAutoChanges: ${JSON.stringify(push.data)}`); this.statusCode(push); } } catch (e: any) { - this.platform.log.error(JSON.stringify(e.message)); - this.platform.debug(`Humidifier ${this.accessory.displayName} - ${JSON.stringify(e)}`); + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushAutoChanges with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushAutoChanges with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushAutoChanges with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } } @@ -429,28 +504,26 @@ export class Humidifier { commandType: 'command', command: 'turnOff', parameter: 'default', - } as any; - - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`Humidifier ${this.accessory.displayName} pushActiveChanges: ${JSON.stringify(payload)}`); + } as payload; + + this.platform.log.info(`Humidifier: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); // Make the API request const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); - this.platform.debug(`Humidifier ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`); + this.platform.debug(`Humidifier: ${this.accessory.displayName} pushActiveChanges: ${JSON.stringify(push.data)}`); this.statusCode(push); } } catch (e: any) { - this.platform.log.error(JSON.stringify(e.message)); - this.platform.debug(`Humidifier ${this.accessory.displayName} - ${JSON.stringify(e)}`); + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushActiveChanges with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushActiveChanges with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Humidifier: ${this.accessory.displayName} failed pushActiveChanges with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } } @@ -460,63 +533,68 @@ export class Humidifier { */ updateHomeKitCharacteristics() { if (this.CurrentRelativeHumidity === undefined) { - this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + this.platform.debug(`Humidifier: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity); - this.platform.device(`Humidifier ${this.accessory.displayName} updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + this.platform.device(`Humidifier: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); } - if (this.WaterLevel === undefined) { - this.platform.debug(`Humidifier ${this.accessory.displayName} WaterLevel: ${this.WaterLevel}`); - } else { - this.service.updateCharacteristic(this.platform.Characteristic.WaterLevel, this.WaterLevel); - this.platform.device(`Humidifier ${this.accessory.displayName} updateCharacteristic WaterLevel: ${this.WaterLevel}`); + if (!this.device.ble) { + if (this.WaterLevel === undefined) { + this.platform.debug(`Humidifier: ${this.accessory.displayName} WaterLevel: ${this.WaterLevel}`); + } else { + this.service.updateCharacteristic(this.platform.Characteristic.WaterLevel, this.WaterLevel); + this.platform.device(`Humidifier: ${this.accessory.displayName} updateCharacteristic WaterLevel: ${this.WaterLevel}`); + } } if (this.CurrentHumidifierDehumidifierState === undefined) { - this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`); + this.platform.debug(`Humidifier: ${this.accessory.displayName} CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.CurrentHumidifierDehumidifierState, this.CurrentHumidifierDehumidifierState); - this.platform.device(`Humidifier ${this.accessory.displayName}` + this.platform.device(`Humidifier: ${this.accessory.displayName}` + ` updateCharacteristic CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`); } if (this.TargetHumidifierDehumidifierState === undefined) { - this.platform.debug(`Humidifier ${this.accessory.displayName} TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}`); + this.platform.debug(`Humidifier: ${this.accessory.displayName} TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.TargetHumidifierDehumidifierState, this.TargetHumidifierDehumidifierState); - this.platform.device(`Humidifier ${this.accessory.displayName}` + this.platform.device(`Humidifier: ${this.accessory.displayName}` + ` updateCharacteristic TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}`); } if (this.Active === undefined) { - this.platform.debug(`Humidifier ${this.accessory.displayName} Active: ${this.Active}`); + this.platform.debug(`Humidifier: ${this.accessory.displayName} Active: ${this.Active}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.Active, this.Active); - this.platform.device(`Humidifier ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`); + this.platform.device(`Humidifier: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`); } if (this.RelativeHumidityHumidifierThreshold === undefined) { - this.platform.debug(`Humidifier ${this.accessory.displayName}` + this.platform.debug(`Humidifier: ${this.accessory.displayName}` + ` RelativeHumidityHumidifierThreshold: ${this.RelativeHumidityHumidifierThreshold}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.RelativeHumidityHumidifierThreshold, this.RelativeHumidityHumidifierThreshold); - this.platform.device(`Humidifier ${this.accessory.displayName}` + this.platform.device(`Humidifier: ${this.accessory.displayName}` + ` updateCharacteristic RelativeHumidityHumidifierThreshold: ${this.RelativeHumidityHumidifierThreshold}`); } - if (!this.device.humidifier?.hide_temperature) { + if (!this.device.humidifier?.hide_temperature && !this.device.ble) { if (this.CurrentTemperature === undefined) { - this.platform.debug(`Humidifier ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); + this.platform.debug(`Humidifier: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); } else { this.temperatureservice!.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.CurrentTemperature); - this.platform.device(`Humidifier ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`); + this.platform.device(`Humidifier: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`); } } } public apiError(e: any) { this.service.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, e); - this.service.updateCharacteristic(this.platform.Characteristic.WaterLevel, e); + if (!this.device.ble) { + this.service.updateCharacteristic(this.platform.Characteristic.WaterLevel, e); + } this.service.updateCharacteristic(this.platform.Characteristic.CurrentHumidifierDehumidifierState, e); this.service.updateCharacteristic(this.platform.Characteristic.TargetHumidifierDehumidifierState, e); this.service.updateCharacteristic(this.platform.Characteristic.Active, e); this.service.updateCharacteristic(this.platform.Characteristic.RelativeHumidityHumidifierThreshold, e); - if (!this.device.humidifier?.hide_temperature) { + if (!this.device.humidifier?.hide_temperature && !this.device.ble) { this.temperatureservice!.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, e); } } @@ -524,28 +602,29 @@ export class Humidifier { private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`Humidifier: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`Humidifier: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`Humidifier: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`Humidifier: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`Humidifier: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`Humidifier: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - this.platform.debug('Command successfully sent.'); + this.platform.debug(`Humidifier: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`Humidifier: ${this.accessory.displayName} Unknown statusCode.`); } } @@ -553,7 +632,7 @@ export class Humidifier { * Handle requests to set the "Target Humidifier Dehumidifier State" characteristic */ private handleTargetHumidifierDehumidifierStateSet(value: CharacteristicValue) { - this.platform.debug(`Humidifier ${this.accessory.displayName} - Set TargetHumidifierDehumidifierState: ${value}`); + this.platform.debug(`Humidifier: ${this.accessory.displayName} TargetHumidifierDehumidifierState: ${value}`); this.TargetHumidifierDehumidifierState = value; this.doHumidifierUpdate.next(); @@ -563,7 +642,7 @@ export class Humidifier { * Handle requests to set the "Active" characteristic */ private handleActiveSet(value: CharacteristicValue) { - this.platform.debug(`Humidifier ${this.accessory.displayName} - Set Active: ${value}`); + this.platform.debug(`Humidifier: ${this.accessory.displayName} Active: ${value}`); this.Active = value; this.doHumidifierUpdate.next(); } @@ -572,7 +651,7 @@ export class Humidifier { * Handle requests to set the "Relative Humidity Humidifier Threshold" characteristic */ private handleRelativeHumidityHumidifierThresholdSet(value: CharacteristicValue) { - this.platform.debug(`Humidifier ${this.accessory.displayName} - Set RelativeHumidityHumidifierThreshold: ${value}`); + this.platform.debug(`Humidifier: ${this.accessory.displayName} RelativeHumidityHumidifierThreshold: ${value}`); this.RelativeHumidityHumidifierThreshold = value; if (this.Active === this.platform.Characteristic.Active.INACTIVE) { diff --git a/src/devices/indoorcam.ts b/src/devices/indoorcam.ts index d0ee7fd4..88cd0455 100644 --- a/src/devices/indoorcam.ts +++ b/src/devices/indoorcam.ts @@ -2,7 +2,7 @@ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; import { interval, Subject } from 'rxjs'; import { debounceTime, skipWhile, tap } from 'rxjs/operators'; -import { DeviceURL, device, devicesConfig, switchbot, deviceStatusResponse } from '../settings'; +import { DeviceURL, device, devicesConfig, deviceStatusResponse, payload } from '../settings'; import { AxiosResponse } from 'axios'; export class IndoorCam { @@ -16,8 +16,9 @@ export class IndoorCam { // OpenAPI Others deviceStatus!: deviceStatusResponse; - // BLE Others - switchbot!: switchbot; + // Config + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; // Updates cameraUpdateInProgress!: boolean; @@ -32,17 +33,6 @@ export class IndoorCam { this.On = false; this.OutletInUse = true; - // BLE Connections - if (device.ble) { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const SwitchBot = require('node-switchbot'); - this.switchbot = new SwitchBot(); - const colon = device.deviceId!.match(/.{1,2}/g); - const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A; - this.device.bleMac = bleMac.toLowerCase(); - this.platform.device(this.device.bleMac.toLowerCase()); - } - // this is subject we use to track when we need to POST changes to the SwitchBot API this.doCameraUpdate = new Subject(); this.cameraUpdateInProgress = false; @@ -102,8 +92,15 @@ export class IndoorCam { try { await this.pushChanges(); } catch (e: any) { - this.platform.log.error(JSON.stringify(e.message)); - this.platform.debug(`Camera ${accessory.displayName} - ${JSON.stringify(e)}`); + this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} failed pushChanges`); + if (this.deviceDebug) { + this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} failed pushChanges,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} failed pushChanges,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } this.cameraUpdateInProgress = false; @@ -118,76 +115,26 @@ export class IndoorCam { default: this.On = false; } - this.platform.debug(`Camera ${this.accessory.displayName} On: ${this.On}`); + this.platform.debug(`Indoor Cam ${this.accessory.displayName} On: ${this.On}`); } - async refreshStatus() { - if (this.device.ble) { - this.platform.device('BLE'); - await this.BLErefreshStatus(); - } else { - this.platform.device('OpenAPI'); - await this.openAPIRefreshStatus(); - } - } - private async BLErefreshStatus() { - this.platform.debug('IndoorCam BLE Device RefreshStatus'); - // eslint-disable-next-line @typescript-eslint/no-var-requires - const Switchbot = require('node-switchbot'); - const switchbot = new Switchbot(); - const colon = this.device.deviceId!.match(/.{1,2}/g); - const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A; - this.device.bleMac = bleMac.toLowerCase(); - this.platform.device(this.device.bleMac!); - switchbot.onadvertisement = (ad: any) => { - this.platform.debug(JSON.stringify(ad, null, ' ')); - this.platform.device(`ad: ${JSON.stringify(ad)}`); - }; - switchbot - .startScan({ - id: this.device.bleMac, - }) - .then(() => { - return switchbot.wait(this.platform.config.options!.refreshRate! * 1000); - }) - .then(() => { - switchbot.stopScan(); - }) - .catch(async (error: any) => { - this.platform.log.error(error); - await this.openAPIRefreshStatus(); - }); - setInterval(() => { - this.platform.log.info('Start scan ' + this.device.deviceName + '(' + this.device.bleMac + ')'); - switchbot - .startScan({ - mode: 'T', - id: bleMac, - }) - .then(() => { - return switchbot.wait(this.platform.config.options!.refreshRate! * 1000); - }) - .then(() => { - switchbot.stopScan(); - this.platform.log.info('Stop scan ' + this.device.deviceName + '(' + this.device.bleMac + ')'); - }) - .catch(async (error: any) => { - this.platform.log.error(error); - await this.openAPIRefreshStatus(); - }); - }, this.platform.config.options!.refreshRate! * 60000); - } - - private async openAPIRefreshStatus() { + private async refreshStatus() { try { this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; - this.platform.device(`Camera ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); + this.platform.device(`Indoor Cam: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); this.parseStatus(); this.updateHomeKitCharacteristics(); } catch (e: any) { - this.platform.log.error(`IndoorCam ${this.accessory.displayName} failed to refresh status, Error Message: ${JSON.stringify(e.message)}`); - this.platform.debug(`IndoorCam ${this.accessory.displayName}, Error: ${JSON.stringify(e)}`); + this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} failed refreshStatus with BLE Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } } @@ -202,7 +149,7 @@ export class IndoorCam { const payload = { commandType: 'command', parameter: 'default', - } as any; + } as payload; if (this.On) { payload.command = 'turnOn'; @@ -210,36 +157,28 @@ export class IndoorCam { payload.command = 'turnOff'; } - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`Camera ${this.accessory.displayName} pushchanges: ${JSON.stringify(payload)}`); + this.platform.log.info(`Indoor Cam: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); // Make the API request const push: any = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); - this.platform.debug(`Camera ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`); + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} pushChanges: ${JSON.stringify(push.data)}`); this.statusCode(push); + this.refreshStatus(); } updateHomeKitCharacteristics() { if (this.On === undefined) { - this.platform.debug(`Camera ${this.accessory.displayName} On: ${this.On}`); + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} On: ${this.On}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.On, this.On); - this.platform.device(`Camera ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.platform.device(`Indoor Cam: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); } if (this.OutletInUse === undefined) { - this.platform.debug(`Camera ${this.accessory.displayName} OutletInUse: ${this.OutletInUse}`); + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} OutletInUse: ${this.OutletInUse}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.OutletInUse, this.OutletInUse); - this.platform.device(`Camera ${this.accessory.displayName} updateCharacteristic OutletInUse: ${this.OutletInUse}`); + this.platform.device(`Indoor Cam: ${this.accessory.displayName} updateCharacteristic OutletInUse: ${this.OutletInUse}`); } } @@ -248,34 +187,32 @@ export class IndoorCam { this.service.updateCharacteristic(this.platform.Characteristic.OutletInUse, e); } - - private statusCode(push: AxiosResponse<{ statusCode: number;}>) { + private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`Indoor Cam: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - if (this.platform.config.options?.debug) { - this.platform.log.info('Command successfully sent.'); - } + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} Unknown statusCode.`); } } @@ -283,7 +220,7 @@ export class IndoorCam { * Handle requests to set the value of the "On" characteristic */ OnSet(value: CharacteristicValue) { - this.platform.debug(`Camera ${this.accessory.displayName} - Set On: ${value}`); + this.platform.debug(`Indoor Cam: ${this.accessory.displayName} On: ${value}`); this.On = value; this.doCameraUpdate.next(); diff --git a/src/devices/meters.ts b/src/devices/meters.ts index 6a2ce7ef..be11e239 100644 --- a/src/devices/meters.ts +++ b/src/devices/meters.ts @@ -35,6 +35,10 @@ export class Meter { fahrenheit!: serviceData['fahrenheit']; temperature!: serviceData['temperature']; + // Config + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; + // Updates meterUpdateInProgress!: boolean; doMeterUpdate: Subject; @@ -45,8 +49,8 @@ export class Meter { public device: device & devicesConfig, ) { // Meter Config - this.platform.device(`[Meter Config] ble: ${device.ble}, unit: ${device.meter?.unit},` - + ` hide_temperature: ${device.meter?.hide_temperature}, hide_humidity: ${device.meter?.hide_humidity}`); + this.platform.device(`Meter: ${this.accessory.displayName} Config: (ble: ${device.ble}, unit: ${device.meter?.unit},` + + ` hide_temperature: ${device.meter?.hide_temperature}, hide_humidity: ${device.meter?.hide_humidity})`); // default placeholders this.BatteryLevel = 0; @@ -55,17 +59,6 @@ export class Meter { this.CurrentRelativeHumidity = 0; this.CurrentTemperature = 0; - // BLE Connection - if (device.ble) { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const SwitchBot = require('node-switchbot'); - this.switchbot = new SwitchBot(); - const colon = device.deviceId!.match(/.{1,2}/g); - const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A; - this.device.bleMac = bleMac.toLowerCase(); - this.platform.device(this.device.bleMac.toLowerCase()); - } - // this is subject we use to track when we need to POST changes to the SwitchBot API this.doMeterUpdate = new Subject(); this.meterUpdateInProgress = false; @@ -170,55 +163,58 @@ export class Meter { */ async parseStatus() { if (this.device.ble) { - this.platform.device('BLE'); await this.BLEparseStatus(); } else { - this.platform.device('OpenAPI'); await this.openAPIparseStatus(); } } private async BLEparseStatus() { - if (this.deviceStatus.body) { - this.BatteryLevel = 100; - } else { - this.BatteryLevel = 10; - } - if (this.BatteryLevel < 15) { - this.StatusLowBattery = 1; - } else { - this.StatusLowBattery = 0; - } - this.platform.debug(`${this.accessory.displayName} - , BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); - } + this.platform.debug(`Meter: ${this.accessory.displayName} BLE parseStatus`); - private async openAPIparseStatus() { - if (this.deviceStatus.body) { - this.BatteryLevel = 100; - } else { - this.BatteryLevel = 10; - } + // Battery + this.BatteryLevel = Number(this.battery); if (this.BatteryLevel < 15) { this.StatusLowBattery = 1; } else { this.StatusLowBattery = 0; } - // Current Relative Humidity + this.platform.debug(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`); + if (!this.device.meter?.hide_humidity) { - if (this.device.ble) { - this.CurrentRelativeHumidity = Number(this.humidity); - } else { - this.CurrentRelativeHumidity = this.deviceStatus.body.humidity!; - } - this.platform.debug(`Meter ${this.accessory.displayName} - Humidity: ${this.CurrentRelativeHumidity}%`); + // Humidity + this.CurrentRelativeHumidity = Number(this.humidity); + this.platform.debug(`Meter: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`); } // Current Temperature if (!this.device.meter?.hide_temperature) { - if (this.device.ble) { - this.CurrentTemperature = Number(this.temperature); + this.CurrentTemperature = Number(this.temperature); + this.platform.debug(`Meter: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c, fahrenheit: ${this.fahrenheit}`); + } + } + + private async openAPIparseStatus() { + if (this.platform.config.credentials?.openToken) { + this.platform.debug(`Meter: ${this.accessory.displayName} OpenAPI parseStatus`); + if (this.deviceStatus.body) { + this.BatteryLevel = 100; + } else { + this.BatteryLevel = 10; + } + if (this.BatteryLevel < 15) { + this.StatusLowBattery = 1; } else { + this.StatusLowBattery = 0; + } + // Current Relative Humidity + if (!this.device.meter?.hide_humidity) { + this.CurrentRelativeHumidity = this.deviceStatus.body.humidity!; + this.platform.debug(`Meter: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`); + } + + // Current Temperature + if (!this.device.meter?.hide_temperature) { if (this.device.meter?.unit === 1) { this.CurrentTemperature = this.toFahrenheit(this.deviceStatus.body.temperature!); } else if (this.device.meter?.unit === 0) { @@ -226,8 +222,8 @@ export class Meter { } else { this.CurrentTemperature = Number(this.deviceStatus.body.temperature); } + this.platform.debug(`Meter: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`); } - this.platform.debug(`Meter ${this.accessory.displayName} - Temperature: ${this.CurrentTemperature}°c`); } } @@ -236,10 +232,8 @@ export class Meter { */ async refreshStatus() { if (this.device.ble) { - this.platform.device('BLE'); await this.BLErefreshStatus(); } else { - this.platform.device('OpenAPI'); await this.openAPIRefreshStatus(); } } @@ -251,12 +245,12 @@ export class Meter { const colon = this.device.deviceId!.match(/.{1,2}/g); const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A; this.device.bleMac = bleMac.toLowerCase(); - this.platform.device(this.device.bleMac!); + this.platform.device(`Meter: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); return switchbot; } private async BLErefreshStatus() { - this.platform.debug('Meter BLE Device RefreshStatus'); + this.platform.debug(`Meter: ${this.accessory.displayName} BLE RefreshStatus`); // eslint-disable-next-line @typescript-eslint/no-var-requires const switchbot = this.connectBLE(); // Start to monitor advertisement packets @@ -271,10 +265,10 @@ export class Meter { this.fahrenheit = ad.serviceData.fahrenheit; this.humidity = ad.serviceData.humidity; this.battery = ad.serviceData.battery; - this.platform.device(`${this.device.bleMac}: ${JSON.stringify(ad.serviceData)}`); - this.platform.device(`${this.accessory.displayName}, Model: ${ad.serviceData.model}, Model Name: ${ad.serviceData.modelName},` - + `Temperature: ${ad.serviceData.temperature}, Fahrenheit: ${ad.serviceData.fahrenheit}, Humidity: ${ad.serviceData.humidity}` - + `Battery: ${ad.serviceData.battery}`); + this.platform.device(`Meter: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); + this.platform.device(`Meter: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName}, ` + + `temperature: ${ad.serviceData.temperature}, fahrenheit: ${ad.serviceData.fahrenheit}, humidity: ${ad.serviceData.humidity}, ` + + `battery: ${ad.serviceData.battery}`); }; // Wait 10 seconds return switchbot.wait(10000); @@ -284,22 +278,42 @@ export class Meter { this.parseStatus(); this.updateHomeKitCharacteristics(); }).catch(async (e: any) => { - this.platform.log.error(`BLE Connection Failed: ${e.message}`); - this.platform.log.warn('Using OpenAPI Connection'); - await this.openAPIRefreshStatus(); + this.platform.log.error(`Meter: ${this.accessory.displayName} failed refreshStatus with BLE Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Meter: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Meter: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + if (this.platform.config.credentials?.openToken) { + this.platform.log.warn(`Meter: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.openAPIRefreshStatus(); + } }); } private async openAPIRefreshStatus() { - try { - this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; - this.platform.debug(`Meter ${this.accessory.displayName} openAPIRefreshStatus: ${JSON.stringify(this.deviceStatus)}`); - this.parseStatus(); - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.platform.log.error(`Meter ${this.accessory.displayName} failed to refresh status, Error Message: ${JSON.stringify(e.message)}`); - this.platform.debug(`Meter ${this.accessory.displayName}, Error: ${JSON.stringify(e)}`); - this.apiError(e); + if (this.platform.config.credentials?.openToken) { + this.platform.debug(`Meter: ${this.accessory.displayName} OpenAPI RefreshStatus`); + try { + this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; + this.platform.debug(`Meter: ${this.accessory.displayName} openAPIRefreshStatus: ${JSON.stringify(this.deviceStatus)}`); + this.parseStatus(); + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.platform.log.error(`Meter: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Meter: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Meter: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + this.apiError(e); + } } } @@ -308,31 +322,31 @@ export class Meter { */ updateHomeKitCharacteristics() { if (this.StatusLowBattery === undefined) { - this.platform.debug(`Meter ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + this.platform.debug(`Meter: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.platform.device(`Meter ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); + this.platform.device(`Meter: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); } if (this.BatteryLevel === undefined) { - this.platform.debug(`Meter ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + this.platform.debug(`Meter: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel); - this.platform.device(`Meter ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); + this.platform.device(`Meter: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); } if (!this.device.meter?.hide_humidity) { if (this.CurrentRelativeHumidity === undefined) { - this.platform.debug(`Meter ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + this.platform.debug(`Meter: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); } else { this.humidityservice?.updateCharacteristic(this.platform.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity); - this.platform.device(`Meter ${this.accessory.displayName} updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); + this.platform.device(`Meter: ${this.accessory.displayName} updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`); } } if (!this.device.meter?.hide_temperature) { if (this.CurrentTemperature === undefined) { - this.platform.debug(`Meter ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); + this.platform.debug(`Meter: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); } else { this.temperatureservice?.updateCharacteristic(this.platform.Characteristic.CurrentTemperature, this.CurrentTemperature); - this.platform.device(`Meter ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`); + this.platform.device(`Meter: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`); } } } diff --git a/src/devices/motion.ts b/src/devices/motion.ts index c4b0ba20..df566507 100644 --- a/src/devices/motion.ts +++ b/src/devices/motion.ts @@ -31,6 +31,10 @@ export class Motion { movement!: serviceData['movement']; lightLevel!: serviceData['lightLevel']; + // Config + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; + // Updates motionUbpdateInProgress!: boolean; doMotionUpdate!: Subject; @@ -43,17 +47,6 @@ export class Motion { // default placeholders this.MotionDetected = false; - // BLE Connection - if (device.ble) { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const SwitchBot = require('node-switchbot'); - this.switchbot = new SwitchBot(); - const colon = device.deviceId!.match(/.{1,2}/g); - const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A; - this.device.bleMac = bleMac.toLowerCase(); - this.platform.device(this.device.bleMac.toLowerCase()); - } - // this is subject we use to track when we need to POST changes to the SwitchBot API this.doMotionUpdate = new Subject(); this.motionUbpdateInProgress = false; @@ -119,17 +112,16 @@ export class Motion { */ async parseStatus() { if (this.device.ble) { - this.platform.device('BLE'); await this.BLEparseStatus(); } else { - this.platform.device('OpenAPI'); await this.openAPIparseStatus(); } } private async BLEparseStatus() { + this.platform.device(`Motion Sensor: ${this.accessory.displayName} BLE parseStatus`); this.MotionDetected = Boolean(this.movement); - this.platform.debug(`${this.accessory.displayName}, MotionDetected: ${this.MotionDetected}`); + this.platform.debug(`Motion Sensor: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`); // Light Level switch (this.lightLevel) { case 'dark': @@ -149,8 +141,11 @@ export class Motion { } private async openAPIparseStatus() { - this.MotionDetected = Boolean(this.deviceStatus.body.moveDetected); - this.platform.debug(`${this.accessory.displayName}, MotionDetected: ${this.MotionDetected}`); + if (this.platform.config.credentials?.openToken) { + this.platform.device(`Motion Sensor: ${this.accessory.displayName} OpenAPI parseStatus`); + this.MotionDetected = Boolean(this.deviceStatus.body.moveDetected); + this.platform.debug(`Motion Sensor: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`); + } } /** @@ -158,10 +153,8 @@ export class Motion { */ async refreshStatus() { if (this.device.ble) { - this.platform.device('BLE'); await this.BLERefreshStatus(); } else { - this.platform.device('OpenAPI'); await this.openAPIRefreshStatus(); } } @@ -173,12 +166,12 @@ export class Motion { const colon = this.device.deviceId!.match(/.{1,2}/g); const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A; this.device.bleMac = bleMac.toLowerCase(); - this.platform.device(this.device.bleMac!); + this.platform.device(`Motion Sensor: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`); return switchbot; } private async BLERefreshStatus() { - this.platform.debug('Motion BLE Device RefreshStatus'); + this.platform.debug(`Motion Sensor: ${this.accessory.displayName} BLE RefreshStatus`); // eslint-disable-next-line @typescript-eslint/no-var-requires const switchbot = this.connectBLE(); // Start to monitor advertisement packets @@ -189,12 +182,12 @@ export class Motion { // Set an event hander switchbot.onadvertisement = (ad: any) => { this.serviceData = ad.serviceData; - this.platform.device(`${this.device.bleMac}: ${JSON.stringify(ad.serviceData)}`); this.movement = ad.serviceData.movement; this.battery = ad.serviceData.battery; this.lightLevel = ad.serviceData.lightLevel; - this.platform.device(`${this.accessory.displayName}, Movement: ${ad.serviceData.movement}, Light Level: ${ad.serviceData.lightLevel},` - + ` Battery: ${ad.serviceData.battery}`); + this.platform.device(`Motion Sensor: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`); + this.platform.device(`Motion Sensor: ${this.accessory.displayName} movement: ${ad.serviceData.movement}, lightLevel: ` + + `${ad.serviceData.lightLevel}, battery: ${ad.serviceData.battery}`); }; // Wait 10 seconds return switchbot.wait(10000); @@ -204,22 +197,42 @@ export class Motion { this.parseStatus(); this.updateHomeKitCharacteristics(); }).catch(async (e: any) => { - this.platform.log.error(`BLE Connection Failed: ${e.message}`); - this.platform.log.warn('Using OpenAPI Connection'); - await this.openAPIRefreshStatus(); + this.platform.log.error(`Motion Sensor: ${this.accessory.displayName} failed refreshStatus with BLE Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Motion Sensor: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Motion Sensor: ${this.accessory.displayName} failed refreshStatus with BLE Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + if (this.platform.config.credentials?.openToken) { + this.platform.log.warn(`Motion Sensor: ${this.accessory.displayName} Using OpenAPI Connection`); + await this.openAPIRefreshStatus(); + } }); } private async openAPIRefreshStatus() { - try { - this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; - this.platform.debug(`Motion ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); - this.parseStatus(); - this.updateHomeKitCharacteristics(); - } catch (e: any) { - this.platform.log.error(`Motion ${this.accessory.displayName} failed to refresh status, Error Message: ${JSON.stringify(e.message)}`); - this.platform.debug(`Motion ${this.accessory.displayName}, Error: ${JSON.stringify(e)}`); - this.apiError(e); + if (this.platform.config.credentials?.openToken) { + this.platform.debug(`Motion Sensor: ${this.accessory.displayName} OpenAPI RefreshStatus`); + try { + this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; + this.platform.debug(`Motion Sensor: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); + this.parseStatus(); + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.platform.log.error(`Motion Sensor: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Motion Sensor: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Motion Sensor: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } + this.apiError(e); + } } } @@ -228,29 +241,30 @@ export class Motion { */ updateHomeKitCharacteristics() { if (this.MotionDetected === undefined) { - this.platform.debug(`Motion ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`); + this.platform.debug(`Motion Sensor: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.MotionDetected, this.MotionDetected); - this.platform.device(`Motion ${this.accessory.displayName} updateCharacteristic MotionDetected: ${this.MotionDetected}`); + this.platform.device(`Motion Sensor: ${this.accessory.displayName} updateCharacteristic MotionDetected: ${this.MotionDetected}`); } if (this.device.ble) { if (this.BatteryLevel === undefined) { - this.platform.debug(`Motion ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); + this.platform.debug(`Motion Sensor: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`); } else { this.batteryService?.updateCharacteristic(this.platform.Characteristic.BatteryLevel, this.BatteryLevel); - this.platform.device(`Motion ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); + this.platform.device(`Motion Sensor: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`); } if (this.StatusLowBattery === undefined) { - this.platform.debug(`Motion ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); + this.platform.debug(`Motion Sensor: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`); } else { this.batteryService?.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, this.StatusLowBattery); - this.platform.device(`Motion ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); + this.platform.device(`Motion Sensor: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`); } if (this.CurrentAmbientLightLevel === undefined) { - this.platform.debug(`Motion ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + this.platform.debug(`Motion Sensor: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); } else { this.lightSensorService?.updateCharacteristic(this.platform.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel); - this.platform.device(`Motion ${this.accessory.displayName} updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); + this.platform.device(`Motion Sensor: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`); } } } diff --git a/src/devices/plugs.ts b/src/devices/plugs.ts index 8d6d9d61..4b1e6995 100644 --- a/src/devices/plugs.ts +++ b/src/devices/plugs.ts @@ -2,7 +2,8 @@ import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; import { interval, Subject } from 'rxjs'; import { debounceTime, skipWhile, tap } from 'rxjs/operators'; -import { DeviceURL, device, devicesConfig, switchbot, deviceStatusResponse } from '../settings'; +import { DeviceURL, device, devicesConfig, deviceStatusResponse, payload } from '../settings'; +import { AxiosResponse } from 'axios'; export class Plug { // Services @@ -15,8 +16,9 @@ export class Plug { // OpenAPI Others deviceStatus!: deviceStatusResponse; - // BLE Others - switchbot!: switchbot; + // Config + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; // Updates plugUpdateInProgress!: boolean; @@ -31,17 +33,6 @@ export class Plug { this.On = false; this.OutletInUse = true; - // BLE Connections - if (device.ble) { - // eslint-disable-next-line @typescript-eslint/no-var-requires - const SwitchBot = require('node-switchbot'); - this.switchbot = new SwitchBot(); - const colon = device.deviceId!.match(/.{1,2}/g); - const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A; - this.device.bleMac = bleMac.toLowerCase(); - this.platform.device(this.device.bleMac.toLowerCase()); - } - // this is subject we use to track when we need to POST changes to the SwitchBot API this.doPlugUpdate = new Subject(); this.plugUpdateInProgress = false; @@ -101,8 +92,15 @@ export class Plug { try { await this.pushChanges(); } catch (e: any) { - this.platform.log.error(JSON.stringify(e.message)); - this.platform.debug(`Plug ${accessory.displayName} - ${JSON.stringify(e)}`); + this.platform.log.error(`Plug: ${this.accessory.displayName} failed pushChanges`); + if (this.deviceDebug) { + this.platform.log.error(`Plug: ${this.accessory.displayName} failed pushChanges,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Plug: ${this.accessory.displayName} failed pushChanges,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } this.plugUpdateInProgress = false; @@ -120,76 +118,22 @@ export class Plug { this.platform.debug(`Plug ${this.accessory.displayName} On: ${this.On}`); } - async refreshStatus() { - if (this.device.ble) { - this.platform.device('BLE'); - await this.BLErefreshStatus(); - } else { - this.platform.device('OpenAPI'); - await this.openAPIRefreshStatus(); - } - } - - private async BLErefreshStatus() { - this.platform.debug('Plug BLE Device RefreshStatus'); - // eslint-disable-next-line @typescript-eslint/no-var-requires - const Switchbot = require('node-switchbot'); - const switchbot = new Switchbot(); - const colon = this.device.deviceId!.match(/.{1,2}/g); - const bleMac = colon!.join(':'); //returns 1A:23:B4:56:78:9A; - this.device.bleMac = bleMac.toLowerCase(); - this.platform.device(this.device.bleMac!); - switchbot.onadvertisement = (ad: any) => { - this.platform.debug(JSON.stringify(ad, null, ' ')); - this.platform.device('ad:', JSON.stringify(ad)); - }; - this.parseStatus(); - this.updateHomeKitCharacteristics(); - switchbot - .startScan({ - id: this.device.bleMac, - }) - .then(() => { - return switchbot.wait(this.platform.config.options!.refreshRate! * 1000); - }) - .then(() => { - switchbot.stopScan(); - }) - .catch(async (error: any) => { - this.platform.log.error(error); - await this.openAPIRefreshStatus(); - }); - setInterval(() => { - this.platform.log.info('Start scan ' + this.device.deviceName + '(' + this.device.bleMac + ')'); - switchbot - .startScan({ - mode: 'T', - id: bleMac, - }) - .then(() => { - return switchbot.wait(this.platform.config.options!.refreshRate! * 1000); - }) - .then(() => { - switchbot.stopScan(); - this.platform.log.info('Stop scan ' + this.device.deviceName + '(' + this.device.bleMac + ')'); - }) - .catch(async (error: any) => { - this.platform.log.error(error); - await this.openAPIRefreshStatus(); - }); - }, this.platform.config.options!.refreshRate! * 60000); - } - - private async openAPIRefreshStatus() { + private async refreshStatus() { try { - this.platform.debug('Plug - Reading', `${DeviceURL}/${this.device.deviceId}/status`); this.deviceStatus = (await this.platform.axios.get(`${DeviceURL}/${this.device.deviceId}/status`)).data; - this.platform.device(`Plug ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); + this.platform.device(`Plug: ${this.accessory.displayName} refreshStatus: ${JSON.stringify(this.deviceStatus)}`); this.parseStatus(); this.updateHomeKitCharacteristics(); } catch (e: any) { - this.platform.log.error(`Plug ${this.accessory.displayName} failed to refresh status, Error Message: ${JSON.stringify(e.message)}`); - this.platform.debug(`Plug ${this.accessory.displayName}, Error: ${JSON.stringify(e)}`); + this.platform.log.error(`Plug: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Plug: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Plug: ${this.accessory.displayName} failed refreshStatus with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } } @@ -204,7 +148,7 @@ export class Plug { const payload = { commandType: 'command', parameter: 'default', - } as any; + } as payload; if (this.On) { payload.command = 'turnOn'; @@ -212,36 +156,27 @@ export class Plug { payload.command = 'turnOff'; } - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`Plug ${this.accessory.displayName} pushchanges: ${JSON.stringify(payload)}`); + this.platform.log.info(`Plug: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); // Make the API request const push: any = (await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload)); - this.platform.debug(`Plug ${this.accessory.displayName} Changes pushed: ${JSON.stringify(push.data)}`); + this.platform.debug(`Plug: ${this.accessory.displayName} pushchanges: ${JSON.stringify(push.data)}`); this.statusCode(push); } updateHomeKitCharacteristics() { if (this.On === undefined) { - this.platform.debug(`Plug ${this.accessory.displayName} On: ${this.On}`); + this.platform.debug(`Plug: ${this.accessory.displayName} On: ${this.On}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.On, this.On); - this.platform.device(`Plug ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + this.platform.device(`Plug: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); } if (this.OutletInUse === undefined) { - this.platform.debug(`Plug ${this.accessory.displayName} OutletInUse: ${this.OutletInUse}`); + this.platform.debug(`Plug: ${this.accessory.displayName} OutletInUse: ${this.OutletInUse}`); } else { this.service.updateCharacteristic(this.platform.Characteristic.OutletInUse, this.OutletInUse); - this.platform.device(`Plug ${this.accessory.displayName} updateCharacteristic OutletInUse: ${this.OutletInUse}`); + this.platform.device(`Plug: ${this.accessory.displayName} updateCharacteristic OutletInUse: ${this.OutletInUse}`); } } @@ -250,34 +185,32 @@ export class Plug { this.service.updateCharacteristic(this.platform.Characteristic.OutletInUse, e); } - - private statusCode(push: { data: { statusCode: any; }; }) { + private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`Plug: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`Plug: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`Plug: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`Plug: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`Plug: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`Plug: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - if (this.platform.config.options?.debug) { - this.platform.log.info('Command successfully sent.'); - } + this.platform.debug(`Plug: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`Plug: ${this.accessory.displayName} Unknown statusCode.`); } } @@ -285,7 +218,7 @@ export class Plug { * Handle requests to set the value of the "On" characteristic */ OnSet(value: CharacteristicValue) { - this.platform.debug(`Plug ${this.accessory.displayName} - Set On: ${value}`); + this.platform.debug(`Plug: ${this.accessory.displayName} - Set On: ${value}`); this.On = value; this.doPlugUpdate.next(); diff --git a/src/irdevices/airconditioners.ts b/src/irdevices/airconditioners.ts index 47947c40..881e43be 100644 --- a/src/irdevices/airconditioners.ts +++ b/src/irdevices/airconditioners.ts @@ -1,7 +1,7 @@ import { AxiosResponse } from 'axios'; import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; -import { irDevicesConfig, DeviceURL, irdevice } from '../settings'; +import { irDevicesConfig, DeviceURL, irdevice, payload } from '../settings'; /** * Platform Accessory @@ -21,7 +21,9 @@ export class AirConditioner { LastTemperature!: CharacteristicValue; CurrentARFanSpeed!: CharacteristicValue; CurrentTemperature!: CharacteristicValue; + TargetHeaterCoolerState?: CharacteristicValue; CurrentHeaterCoolerState!: CharacteristicValue; + HeatingThresholdTemperature?: CharacteristicValue; // Others Busy: any; @@ -33,6 +35,10 @@ export class AirConditioner { static MODE_COOL: number; static MODE_HEAT: number; + // Config + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; + constructor( private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, @@ -72,7 +78,6 @@ export class AirConditioner { minStep: 0.01, }) .onGet((value: CharacteristicValue) => { - this.platform.device(`onGet: ${this.CurrentTemperature}`); return this.CurrentTemperatureGet(value); }); @@ -136,7 +141,6 @@ export class AirConditioner { this.CurrentFanSpeed = Number(value) + 1; } this.pushAirConditionerStatusChanges(); - this.service.updateCharacteristic(this.platform.Characteristic.RotationSpeed, this.CurrentFanSpeed || 1); this.RotationSpeed = this.CurrentFanSpeed || 1; } @@ -150,7 +154,7 @@ export class AirConditioner { } private ActiveSet(value: CharacteristicValue) { - this.platform.debug(`${this.accessory.displayName} Set Active: ${value}`); + this.platform.debug(`Air Conditioner: ${this.accessory.displayName} Set Active: ${value}`); if (value === this.platform.Characteristic.Active.INACTIVE) { this.pushAirConditionerOffChanges(); @@ -158,24 +162,60 @@ export class AirConditioner { this.pushAirConditionerOnChanges(); } this.Active = value; - if (this.Active !== undefined) { - this.service.updateCharacteristic(this.platform.Characteristic.Active, this.Active); + } + + private updateHomeKitCharacteristics() { + if (this.Active === undefined) { + this.platform.debug(`Air Conditioner: ${this.accessory.displayName} Active: ${this.Active}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.Active, this.Active); + this.platform.device(`Air Conditioner: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`); + } + if (this.RotationSpeed === undefined) { + this.platform.debug(`Air Conditioner: ${this.accessory.displayName} RotationSpeed: ${this.RotationSpeed}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.RotationSpeed, this.RotationSpeed); + this.platform.device(`Air Conditioner: ${this.accessory.displayName} updateCharacteristic RotationSpeed: ${this.RotationSpeed}`); + } + if (this.CurrentTemperature === undefined) { + this.platform.debug(`Air Conditioner: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.Active, this.CurrentTemperature); + this.platform.device(`Air Conditioner: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`); + } + if (this.TargetHeaterCoolerState === undefined) { + this.platform.debug(`Air Conditioner: ${this.accessory.displayName} TargetHeaterCoolerState: ${this.TargetHeaterCoolerState}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.TargetHeaterCoolerState, this.TargetHeaterCoolerState); + this.platform.device(`Air Conditioner: ${this.accessory.displayName}` + + ` updateCharacteristic TargetHeaterCoolerState: ${this.TargetHeaterCoolerState}`); + } + if (this.CurrentHeaterCoolerState === undefined) { + this.platform.debug(`Air Conditioner: ${this.accessory.displayName} CurrentHeaterCoolerState: ${this.CurrentHeaterCoolerState}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, this.CurrentHeaterCoolerState); + this.platform.device(`Air Conditioner: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentHeaterCoolerState: ${this.CurrentHeaterCoolerState}`); + } + if (this.HeatingThresholdTemperature === undefined) { + this.platform.debug(`Air Conditioner: ${this.accessory.displayName} HeatingThresholdTemperature: ${this.HeatingThresholdTemperature}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.HeatingThresholdTemperature, this.HeatingThresholdTemperature); + this.platform.device(`Air Conditioner: ${this.accessory.displayName}` + + ` updateCharacteristic HeatingThresholdTemperature: ${this.HeatingThresholdTemperature}`); } } private CurrentTemperatureGet(value: CharacteristicValue) { this.platform.debug('Trigger Get CurrentTemperture'); - if (this.CurrentTemperature) { - this.CurrentTemperature; - this.service - .getCharacteristic(this.platform.Characteristic.CurrentTemperature).updateValue(this.CurrentTemperature); - } else { - this.CurrentTemperature = 24; - this.service - .getCharacteristic(this.platform.Characteristic.CurrentTemperature).updateValue(this.CurrentTemperature); - } - return (this.CurrentTemperature = value); + value = Number(this.CurrentTemperature) || 24; + + this.service + .getCharacteristic(this.platform.Characteristic.CurrentTemperature) + .updateValue(value); + + return value; } private TargetHeaterCoolerStateSet(value: CharacteristicValue) { @@ -205,32 +245,22 @@ export class AirConditioner { } else { this.CurrentHeaterCoolerState = this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE; } - this.platform.device(`CurrentHeaterCoolerStateGet: ${this.CurrentHeaterCoolerState}`); + this.platform.device(`Air Conditioner: ${this.accessory.displayName} CurrentHeaterCoolerState: ${this.CurrentHeaterCoolerState}`); return this.CurrentHeaterCoolerState; } private HeatingThresholdTemperatureGet() { - if (this.CurrentTemperature) { - this.CurrentTemperature; - } else { - this.CurrentTemperature = 24; - } - this.platform.device(`HeatingThresholdTemperatureGet: ${this.CurrentTemperature}`); + this.CurrentTemperature = this.CurrentTemperature || 24; + this.platform.device(`Air Conditioner: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`); return this.CurrentTemperature; } private HeatingThresholdTemperatureSet(value: CharacteristicValue) { - this.platform.device(`Before HeatingThresholdTemperatureSet CurrentTemperature: ${this.CurrentTemperature},` - + ` HeatingThresholdTemperatureSet LastTemperature: ${this.LastTemperature}`); this.pushAirConditionerStatusChanges(); - this.LastTemperature = this.CurrentTemperature; - if (this.CurrentTemperature) { - this.CurrentTemperature = value; - } else { - this.CurrentTemperature = 24; - } - this.platform.device(`After HeatingThresholdTemperatureSet CurrentTemperature: ${this.CurrentTemperature},` - + ` HeatingThresholdTemperatureSet LastTemperature: ${this.LastTemperature}`); + this.LastTemperature = Number(this.CurrentTemperature); + this.CurrentTemperature = Number(value); + this.platform.device(`Air Conditioner: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature},` + + ` LastTemperature: ${this.LastTemperature}`); } /** @@ -267,10 +297,7 @@ export class AirConditioner { async pushAirConditionerStatusChanges() { if (!this.Busy) { this.Busy = true; - this.service.updateCharacteristic( - this.platform.Characteristic.CurrentHeaterCoolerState, - this.platform.Characteristic.CurrentHeaterCoolerState.IDLE, - ); + this.CurrentHeaterCoolerState = this.platform.Characteristic.CurrentHeaterCoolerState.IDLE; } clearTimeout(this.Timeout); @@ -293,74 +320,67 @@ export class AirConditioner { if (this.Active === 1) { if ((this.CurrentTemperature || 24) < (this.LastTemperature || 30)) { - this.service.updateCharacteristic( - this.platform.Characteristic.CurrentHeaterCoolerState, - this.platform.Characteristic.CurrentHeaterCoolerState.COOLING, - ); + this.CurrentHeaterCoolerState = this.platform.Characteristic.CurrentHeaterCoolerState.COOLING; } else { - this.service.updateCharacteristic( - this.platform.Characteristic.CurrentHeaterCoolerState, - this.platform.Characteristic.CurrentHeaterCoolerState.HEATING, - ); + this.CurrentHeaterCoolerState = this.platform.Characteristic.CurrentHeaterCoolerState.HEATING; } } else { - this.service.updateCharacteristic( - this.platform.Characteristic.CurrentHeaterCoolerState, - this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE, - ); + this.CurrentHeaterCoolerState = this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE; } await this.pushChanges(payload); } - public async pushChanges(payload: any) { + public async pushChanges(payload: payload) { try { - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`${this.accessory.displayName} pushChanges - ${JSON.stringify(payload)}`); + this.platform.log.info(`Air Conditioner: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); // Make the API request const push = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); - this.platform.debug(`${this.accessory.displayName} Changes pushed - ${push.data}`); + this.platform.debug(`Air Conditioner: ${this.accessory.displayName} pushChanges: ${push.data}`); this.statusCode(push); - } catch (e) { + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} failed pushChanges`); + if (this.deviceDebug) { + this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} failed pushChanges,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} failed pushChanges,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } } - private statusCode(push: AxiosResponse<{ statusCode: number;}>) { + private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`Air Conditioner: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - this.platform.debug('Command successfully sent.'); + this.platform.debug(`Air Conditioner: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`Air Conditioner: ${this.accessory.displayName} Unknown statusCode.`); } } diff --git a/src/irdevices/airpurifiers.ts b/src/irdevices/airpurifiers.ts index 6178afa6..f02631f5 100644 --- a/src/irdevices/airpurifiers.ts +++ b/src/irdevices/airpurifiers.ts @@ -1,7 +1,7 @@ import { AxiosResponse } from 'axios'; import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; -import { irDevicesConfig, DeviceURL, irdevice } from '../settings'; +import { irDevicesConfig, DeviceURL, irdevice, payload } from '../settings'; /** * Platform Accessory @@ -21,6 +21,7 @@ export class AirPurifier { CurrentAPFanSpeed!: CharacteristicValue; CurrentTemperature!: CharacteristicValue; CurrentAirPurifierState!: CharacteristicValue; + CurrentHeaterCoolerState!: CharacteristicValue; // Others Busy: any; @@ -32,6 +33,10 @@ export class AirPurifier { CurrentFanSpeed!: number; static PURIFYING_AIR: number; + // Config + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; + constructor( private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, @@ -69,15 +74,35 @@ export class AirPurifier { } private ActiveSet(value: CharacteristicValue) { - this.platform.debug(`${this.accessory.displayName} Set Active: ${value}`); + this.platform.debug(`Air Purifier: ${this.accessory.displayName} Set Active: ${value}`); if (value === this.platform.Characteristic.Active.INACTIVE) { this.pushAirConditionerOffChanges(); } else { this.pushAirConditionerOnChanges(); } this.Active = value; - if (this.Active !== undefined) { - this.service.updateCharacteristic(this.platform.Characteristic.Active, this.Active); + } + + private updateHomeKitCharacteristics() { + if (this.Active === undefined) { + this.platform.debug(`Air Purifier: ${this.accessory.displayName} Active: ${this.Active}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.Active, this.Active); + this.platform.device(`Air Purifier: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`); + } + if (this.CurrentAirPurifierState === undefined) { + this.platform.debug(`Air Purifier: ${this.accessory.displayName} CurrentAirPurifierState: ${this.CurrentAirPurifierState}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.CurrentAirPurifierState, this.CurrentAirPurifierState); + this.platform.device(`Air Purifier: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentAirPurifierState: ${this.CurrentAirPurifierState}`); + } + if (this.CurrentHeaterCoolerState === undefined) { + this.platform.debug(`Air Purifier: ${this.accessory.displayName} CurrentHeaterCoolerState: ${this.CurrentHeaterCoolerState}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.CurrentHeaterCoolerState, this.CurrentHeaterCoolerState); + this.platform.device(`Air Purifier: ${this.accessory.displayName}` + + ` updateCharacteristic CurrentHeaterCoolerState: ${this.CurrentHeaterCoolerState}`); } } @@ -123,7 +148,7 @@ export class AirPurifier { commandType: 'command', parameter: 'default', command: 'turnOn', - } as any; + } as payload; await this.pushChanges(payload); } } @@ -134,7 +159,7 @@ export class AirPurifier { commandType: 'command', parameter: 'default', command: 'turnOff', - } as any; + } as payload; await this.pushChanges(payload); } } @@ -142,10 +167,7 @@ export class AirPurifier { async pushAirConditionerStatusChanges() { if (!this.Busy) { this.Busy = true; - this.service.updateCharacteristic( - this.platform.Characteristic.CurrentHeaterCoolerState, - this.platform.Characteristic.CurrentHeaterCoolerState.IDLE, - ); + this.CurrentHeaterCoolerState = this.platform.Characteristic.CurrentHeaterCoolerState.IDLE; } clearTimeout(this.Timeout); @@ -157,7 +179,7 @@ export class AirPurifier { const payload = { commandType: 'command', command: 'setAll', - } as any; + } as payload; this.CurrentAPTemp = this.CurrentTemperature || 24; this.CurrentAPMode = this.CurrentMode || 1; @@ -167,74 +189,67 @@ export class AirPurifier { if (this.Active === 1) { if ((this.CurrentTemperature || 24) < (this.LastTemperature || 30)) { - this.service.updateCharacteristic( - this.platform.Characteristic.CurrentHeaterCoolerState, - this.platform.Characteristic.CurrentHeaterCoolerState.COOLING, - ); + this.CurrentHeaterCoolerState = this.platform.Characteristic.CurrentHeaterCoolerState.COOLING; } else { - this.service.updateCharacteristic( - this.platform.Characteristic.CurrentHeaterCoolerState, - this.platform.Characteristic.CurrentHeaterCoolerState.HEATING, - ); + this.CurrentHeaterCoolerState = this.platform.Characteristic.CurrentHeaterCoolerState.HEATING; } } else { - this.service.updateCharacteristic( - this.platform.Characteristic.CurrentHeaterCoolerState, - this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE, - ); + this.CurrentHeaterCoolerState = this.platform.Characteristic.CurrentHeaterCoolerState.INACTIVE; } await this.pushChanges(payload); } - public async pushChanges(payload: any) { + public async pushChanges(payload: payload) { try { - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`${this.accessory.displayName} pushChanges - ${JSON.stringify(payload)}`); + this.platform.log.info(`Air Purifier: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); // Make the API request const push = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); - this.platform.debug(`${this.accessory.displayName} Changes pushed - ${push.data}`); + this.platform.debug(`Air Purifier: ${this.accessory.displayName} pushChanges: ${push.data}`); this.statusCode(push); - } catch (e) { + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.platform.log.error(`Air Purifier: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Air Purifier: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Air Purifier: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } } - private statusCode(push: AxiosResponse<{ statusCode: number;}>) { + private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`Air Purifier: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`Air Purifier: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`Air Purifier: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`Air Purifier: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`Air Purifier: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`Air Purifier: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - this.platform.debug('Command successfully sent.'); + this.platform.debug(`Air Purifier: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`Air Purifier: ${this.accessory.displayName} Unknown statusCode.`); } } diff --git a/src/irdevices/cameras.ts b/src/irdevices/cameras.ts index eb74723c..99f33458 100644 --- a/src/irdevices/cameras.ts +++ b/src/irdevices/cameras.ts @@ -1,7 +1,7 @@ import { AxiosResponse } from 'axios'; import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; -import { irDevicesConfig, DeviceURL, irdevice } from '../settings'; +import { irDevicesConfig, DeviceURL, irdevice, payload } from '../settings'; /** * Platform Accessory @@ -15,6 +15,10 @@ export class Camera { // Characteristic Values On!: CharacteristicValue; + // Config + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; + constructor( private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, @@ -46,7 +50,7 @@ export class Camera { } private OnSet(value: CharacteristicValue) { - this.platform.debug(`${this.accessory.displayName} Set On: ${value}`); + this.platform.debug(`Camera: ${this.accessory.displayName} On: ${value}`); this.On = value; if (this.On) { this.pushOnChanges(); @@ -55,6 +59,15 @@ export class Camera { } } + private updateHomeKitCharacteristics() { + if (this.On === undefined) { + this.platform.debug(`Camera: ${this.accessory.displayName} On: ${this.On}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.On, this.On); + this.platform.device(`Camera: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + } + } + /** * Pushes the requested changes to the SwitchBot API * deviceType commandType Command command parameter Description @@ -71,7 +84,7 @@ export class Camera { commandType: 'command', parameter: 'default', command: 'turnOn', - } as any; + } as payload; await this.pushChanges(payload); } } @@ -82,59 +95,61 @@ export class Camera { commandType: 'command', parameter: 'default', command: 'turnOff', - } as any; + } as payload; await this.pushChanges(payload); } } - public async pushChanges(payload: any) { + public async pushChanges(payload: payload) { try { - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`${this.accessory.displayName} pushChanges - ${JSON.stringify(payload)}`); + this.platform.log.info(`Camera: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); // Make the API request const push = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); - this.platform.debug(`${this.accessory.displayName} Changes pushed - ${push.data}`); + this.platform.debug(`Camera: ${this.accessory.displayName} pushChanges: ${push.data}`); this.statusCode(push); - } catch (e) { + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.platform.log.error(`Camera: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Camera: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Camera: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } } - private statusCode(push: AxiosResponse<{ statusCode: number;}>) { + private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`Camera: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`Camera: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`Camera: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`Camera: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`Camera: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`Camera: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - this.platform.debug('Command successfully sent.'); + this.platform.debug(`Camera: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`Camera: ${this.accessory.displayName} Unknown statusCode.`); } } diff --git a/src/irdevices/fans.ts b/src/irdevices/fans.ts index 8f5cdc3e..f46063f4 100644 --- a/src/irdevices/fans.ts +++ b/src/irdevices/fans.ts @@ -1,7 +1,7 @@ import { AxiosResponse } from 'axios'; import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; -import { DeviceURL, irdevice, deviceStatusResponse, irDevicesConfig } from '../settings'; +import { DeviceURL, irdevice, deviceStatusResponse, irDevicesConfig, payload } from '../settings'; /** * Platform Accessory @@ -26,6 +26,8 @@ export class Fan { minStep?: number; minValue?: number; maxValue?: number; + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; constructor( private readonly platform: SwitchBotPlatform, @@ -86,11 +88,10 @@ export class Fan { !device.irfan?.swing_mode) { const characteristic = this.service.getCharacteristic(this.platform.Characteristic.RotationSpeed); this.service.removeCharacteristic(characteristic); - this.platform.log.warn('Rotation Speed Characteristic was removed.'); + this.platform.device(`Fan: ${this.accessory.displayName} Rotation Speed Characteristic was removed.`); } else { - this.platform.debug( - 'Rotation Speed Characteristic was not removed or not added. To Remove Chracteristic, Clear Cache on this Accessory.', - ); + // eslint-disable-next-line max-len + this.platform.device(`Fan: ${this.accessory.displayName} Rotation Speed Characteristic was not removed or not added. To Remove Chracteristic, Clear Cache on this Accessory.`); } if (device.irfan?.swing_mode) { @@ -99,16 +100,16 @@ export class Fan { } else if (this.service.testCharacteristic(this.platform.Characteristic.SwingMode) && !device.irfan?.swing_mode) { const characteristic = this.service.getCharacteristic(this.platform.Characteristic.SwingMode); this.service.removeCharacteristic(characteristic); - this.platform.log.warn('Swing Mode Characteristic was removed.'); + this.platform.device(`Fan: ${this.accessory.displayName} Swing Mode Characteristic was removed.`); } else { - this.platform.debug( - 'Swing Mode Characteristic was not removed or not added. To Remove Chracteristic, Clear Cache on this Accessory.', - ); + // eslint-disable-next-line max-len + this.platform.device(`Fan: ${this.accessory.displayName} Swing Mode Characteristic was not removed or not added. To Remove Chracteristic, Clear Cache on this Accessory.`); + } } private SwingModeSet(value: CharacteristicValue) { - this.platform.debug(`${this.accessory.displayName} Set SwingMode: ${value}`); + this.platform.debug(`Fan: ${this.accessory.displayName} SwingMode: ${value}`); if (value > this.SwingMode) { this.SwingMode = 1; this.pushFanOnChanges(); @@ -119,13 +120,31 @@ export class Fan { this.pushFanSwingChanges(); } this.SwingMode = value; - if (this.SwingMode !== undefined) { - this.service.updateCharacteristic(this.platform.Characteristic.SwingMode, this.SwingMode); + } + + private updateHomeKitCharacteristics() { + if (this.Active === undefined) { + this.platform.debug(`Fan: ${this.accessory.displayName} Active: ${this.Active}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.Active, this.Active); + this.platform.device(`Fan: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`); + } + if (this.SwingMode === undefined) { + this.platform.debug(`Fan: ${this.accessory.displayName} SwingMode: ${this.SwingMode}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.SwingMode, this.SwingMode); + this.platform.device(`Fan: ${this.accessory.displayName} updateCharacteristic SwingMode: ${this.SwingMode}`); + } + if (this.RotationSpeed === undefined) { + this.platform.debug(`Fan: ${this.accessory.displayName} RotationSpeed: ${this.RotationSpeed}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.RotationSpeed, this.RotationSpeed); + this.platform.device(`Fan: ${this.accessory.displayName} updateCharacteristic RotationSpeed: ${this.RotationSpeed}`); } } private RotationSpeedSet(value: CharacteristicValue) { - this.platform.debug(`${this.accessory.displayName} Set Active: ${value}`); + this.platform.debug(`Fan: ${this.accessory.displayName} RotationSpeed: ${value}`); if (value > this.RotationSpeed) { this.RotationSpeed = 1; this.pushFanSpeedUpChanges(); @@ -135,22 +154,16 @@ export class Fan { this.pushFanSpeedDownChanges(); } this.RotationSpeed = value; - if (this.RotationSpeed !== undefined) { - this.service.updateCharacteristic(this.platform.Characteristic.RotationSpeed, this.RotationSpeed); - } } private ActiveSet(value: CharacteristicValue) { - this.platform.debug(`${this.accessory.displayName} Set Active: ${value}`); + this.platform.debug(`Fan: ${this.accessory.displayName} Active: ${value}`); if (value === this.platform.Characteristic.Active.INACTIVE) { this.pushFanOffChanges(); } else { this.pushFanOnChanges(); } this.Active = value; - if (this.Active !== undefined) { - this.service.updateCharacteristic(this.platform.Characteristic.Active, this.Active); - } } /** @@ -168,7 +181,7 @@ export class Fan { commandType: 'command', parameter: 'default', command: 'turnOn', - } as any; + } as payload; await this.pushTVChanges(payload); } } @@ -178,7 +191,7 @@ export class Fan { commandType: 'command', parameter: 'default', command: 'turnOff', - } as any; + } as payload; await this.pushTVChanges(payload); } @@ -187,7 +200,7 @@ export class Fan { commandType: 'command', parameter: 'default', command: 'highSpeed', - } as any; + } as payload; await this.pushTVChanges(payload); } @@ -196,7 +209,7 @@ export class Fan { commandType: 'command', parameter: 'default', command: 'lowSpeed', - } as any; + } as payload; await this.pushTVChanges(payload); } @@ -205,58 +218,60 @@ export class Fan { commandType: 'command', parameter: 'default', command: 'swing', - } as any; + } as payload; await this.pushTVChanges(payload); } - public async pushTVChanges(payload: any) { + public async pushTVChanges(payload: payload) { try { - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`TV ${this.accessory.displayName} pushChanges - ${JSON.stringify(payload)}`); + this.platform.log.info(`Fan: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); // Make the API request const push = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); - this.platform.debug(`TV ${this.accessory.displayName} Changes pushed - ${push.data}`); + this.platform.debug(`Fan: ${this.accessory.displayName} pushTVChanges: ${push.data}`); this.statusCode(push); - } catch (e) { + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.platform.log.error(`Fan: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Fan: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Fan: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } } - private statusCode(push: AxiosResponse<{ statusCode: number;}>) { + private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`Fan: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`Fan: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`Fan: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`Fan: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`Fan: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`Fan: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - this.platform.debug('Command successfully sent.'); + this.platform.debug(`Fan: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`Fan: ${this.accessory.displayName} Unknown statusCode.`); } } diff --git a/src/irdevices/lights.ts b/src/irdevices/lights.ts index 20427778..9a5f2579 100644 --- a/src/irdevices/lights.ts +++ b/src/irdevices/lights.ts @@ -1,7 +1,7 @@ import { AxiosResponse } from 'axios'; import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; -import { irDevicesConfig, DeviceURL, irdevice } from '../settings'; +import { irDevicesConfig, DeviceURL, irdevice, payload } from '../settings'; /** * Platform Accessory @@ -15,6 +15,10 @@ export class Light { // Characteristic Values On!: CharacteristicValue; + // Config + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; + constructor( private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, @@ -61,14 +65,22 @@ export class Light { } private OnSet(value: CharacteristicValue) { - this.platform.debug(`${this.device.remoteType} ${this.accessory.displayName} Set On: ${value}`); + this.platform.debug(`Light: ${this.accessory.displayName} On: ${value}`); this.On = value; if (this.On) { this.pushLightOnChanges(); } else { this.pushLightOffChanges(); } - this.service.updateCharacteristic(this.platform.Characteristic.Active, this.On); + } + + private updateHomeKitCharacteristics() { + if (this.On === undefined) { + this.platform.debug(`Light: ${this.accessory.displayName} On: ${this.On}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.On, this.On); + this.platform.device(`Light: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + } } /** @@ -87,7 +99,7 @@ export class Light { commandType: 'command', parameter: 'default', command: 'turnOn', - } as any; + } as payload; await this.pushChanges(payload); } } @@ -98,7 +110,7 @@ export class Light { commandType: 'command', parameter: 'default', command: 'turnOff', - } as any; + } as payload; await this.pushChanges(payload); } } @@ -108,7 +120,7 @@ export class Light { commandType: 'command', parameter: 'default', command: 'brightnessUp', - } as any; + } as payload; await this.pushChanges(payload); } @@ -117,59 +129,61 @@ export class Light { commandType: 'command', parameter: 'default', command: 'brightnessDown', - } as any; + } as payload; await this.pushChanges(payload); } - public async pushChanges(payload: any) { + public async pushChanges(payload: payload) { try { - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`${this.accessory.displayName} pushChanges - ${JSON.stringify(payload)}`); + this.platform.log.info(`Light: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); // Make the API request const push = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); - this.platform.debug(`${this.accessory.displayName} Changes pushed - ${push.data}`); + this.platform.debug(`Light: ${this.accessory.displayName} pushChanges: ${push.data}`); this.statusCode(push); - } catch (e) { + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.platform.log.error(`Light: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Light: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Light: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } } - private statusCode(push: AxiosResponse<{ statusCode: number;}>) { + private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`Light: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`Light: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`Light: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`Light: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`Light: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`Light: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - this.platform.debug('Command successfully sent.'); + this.platform.debug(`Light: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`Light: ${this.accessory.displayName} Unknown statusCode.`); } } diff --git a/src/irdevices/others.ts b/src/irdevices/others.ts index 2216dde4..277f0ec2 100644 --- a/src/irdevices/others.ts +++ b/src/irdevices/others.ts @@ -1,7 +1,7 @@ import { AxiosResponse } from 'axios'; import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; -import { irDevicesConfig, DeviceURL, irdevice } from '../settings'; +import { irDevicesConfig, DeviceURL, irdevice, payload } from '../settings'; /** * Platform Accessory @@ -15,6 +15,10 @@ export class Others { // Characteristic Values Active!: CharacteristicValue; + // Config + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; + constructor( private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, @@ -38,18 +42,27 @@ export class Others { this.service.getCharacteristic(this.platform.Characteristic.Active).onSet(this.ActiveSet.bind(this)); } else { accessory.removeService(this.service!); - this.platform.log.error('No Device Type Set'); + this.platform.log.error(`Other: ${this.accessory.displayName} No Device Type Set, deviceType: ${device.other?.deviceType}`); } } private ActiveSet(value: CharacteristicValue) { - this.platform.debug(`${this.accessory.displayName} Set On: ${value}`); - this.Active = value; + this.platform.debug(`Other: ${this.accessory.displayName} On: ${value}`); if (this.Active) { this.pushOnChanges(); } else { this.pushOffChanges(); } + this.Active = value; + } + + private updateHomeKitCharacteristics() { + if (this.Active === undefined) { + this.platform.debug(`Other: ${this.accessory.displayName} Active: ${this.Active}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.Active, this.Active); + this.platform.device(`Other: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`); + } } /** @@ -71,17 +84,17 @@ export class Others { commandType: 'customize', parameter: 'default', command: `${this.device.other.commandOn}`, - } as any; + } as payload; await this.pushChanges(payload); } } else { - this.platform.log.error('On Command not set.'); + this.platform.log.error(`Other: ${this.accessory.displayName} On Command not set, commandOn: ${this.device.other.commandOn}`); } } else { - this.platform.log.error('On Command not set.'); + this.platform.log.error(`Other: ${this.accessory.displayName} On Command not set, other: ${this.device.other}`); } } else { - this.platform.log.error('On Command not set.'); + this.platform.log.error(`Other: ${this.accessory.displayName} On Command not set`); } } @@ -94,68 +107,70 @@ export class Others { commandType: 'customize', parameter: 'default', command: `${this.device.other.commandOff}`, - } as any; + } as payload; await this.pushChanges(payload); } } else { - this.platform.log.error('Off Command not set.'); + this.platform.log.error(`Other: ${this.accessory.displayName} Off Command not set, commandOff: ${this.device.other.commandOff}`); } } else { - this.platform.log.error('Off Command not set.'); + this.platform.log.error(`Other: ${this.accessory.displayName} Off Command not set, other: ${this.device.other}`); } } else { - this.platform.log.error('Off Command not set.'); + this.platform.log.error(`Other: ${this.accessory.displayName} Off Command not set.`); } } - public async pushChanges(payload: any) { + public async pushChanges(payload: payload) { try { - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`${this.accessory.displayName} pushChanges - ${JSON.stringify(payload)}`); + this.platform.log.info(`Other: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); // Make the API request const push = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); - this.platform.debug(`${this.accessory.displayName} Changes pushed - ${push.data}`); + this.platform.debug(`Other: ${this.accessory.displayName} pushChanges: ${push.data}`); this.statusCode(push); - } catch (e) { + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.platform.log.error(`Other: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Other: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Other: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } } - private statusCode(push: AxiosResponse<{ statusCode: number;}>) { + private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`Other: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`Other: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`Other: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`Other: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`Other: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`Other: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - this.platform.debug('Command successfully sent.'); + this.platform.debug(`Other: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`Other: ${this.accessory.displayName} Unknown statusCode.`); } } diff --git a/src/irdevices/tvs.ts b/src/irdevices/tvs.ts index 042e8061..d2981117 100644 --- a/src/irdevices/tvs.ts +++ b/src/irdevices/tvs.ts @@ -1,7 +1,7 @@ import { AxiosResponse } from 'axios'; import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; -import { DeviceURL, irdevice, deviceStatusResponse, irDevicesConfig } from '../settings'; +import { DeviceURL, irdevice, deviceStatusResponse, irDevicesConfig, payload } from '../settings'; /** * Platform Accessory @@ -20,6 +20,10 @@ export class TV { // Others deviceStatus!: deviceStatusResponse; + // Config + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; + constructor( private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, @@ -117,7 +121,7 @@ export class TV { } private VolumeSelectorSet(value: CharacteristicValue) { - this.platform.debug(`${this.accessory.displayName} Set VolumeSelector: ${value}`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} VolumeSelector: ${value}`); if (value === this.platform.Characteristic.VolumeSelector.INCREMENT) { this.pushVolumeUpChanges(); } else { @@ -128,61 +132,61 @@ export class TV { private RemoteKeySet(value: CharacteristicValue) { switch (value) { case this.platform.Characteristic.RemoteKey.REWIND: { - this.platform.debug(`${this.accessory.displayName} Set Remote Key Pressed: REWIND`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Set Remote Key Pressed: REWIND`); break; } case this.platform.Characteristic.RemoteKey.FAST_FORWARD: { - this.platform.debug(`${this.accessory.displayName} Set Remote Key Pressed: FAST_FORWARD`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Set Remote Key Pressed: FAST_FORWARD`); break; } case this.platform.Characteristic.RemoteKey.NEXT_TRACK: { - this.platform.debug(`${this.accessory.displayName} Set Remote Key Pressed: NEXT_TRACK`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Set Remote Key Pressed: NEXT_TRACK`); break; } case this.platform.Characteristic.RemoteKey.PREVIOUS_TRACK: { - this.platform.debug(`${this.accessory.displayName} Set Remote Key Pressed: PREVIOUS_TRACK`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Set Remote Key Pressed: PREVIOUS_TRACK`); break; } case this.platform.Characteristic.RemoteKey.ARROW_UP: { - this.platform.debug(`${this.accessory.displayName} Set Remote Key Pressed: ARROW_UP`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Set Remote Key Pressed: ARROW_UP`); //this.pushUpChanges(); break; } case this.platform.Characteristic.RemoteKey.ARROW_DOWN: { - this.platform.debug(`${this.accessory.displayName} Set Remote Key Pressed: ARROW_DOWN`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Set Remote Key Pressed: ARROW_DOWN`); //this.pushDownChanges(); break; } case this.platform.Characteristic.RemoteKey.ARROW_LEFT: { - this.platform.debug(`${this.accessory.displayName} Set Remote Key Pressed: ARROW_LEFT`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Set Remote Key Pressed: ARROW_LEFT`); //this.pushLeftChanges(); break; } case this.platform.Characteristic.RemoteKey.ARROW_RIGHT: { - this.platform.debug(`${this.accessory.displayName} Set Remote Key Pressed: ARROW_RIGHT`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Set Remote Key Pressed: ARROW_RIGHT`); //this.pushRightChanges(); break; } case this.platform.Characteristic.RemoteKey.SELECT: { - this.platform.debug(`${this.accessory.displayName} Set Remote Key Pressed: SELECT`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Set Remote Key Pressed: SELECT`); //this.pushOkChanges(); break; } case this.platform.Characteristic.RemoteKey.BACK: { - this.platform.debug(`${this.accessory.displayName} Set Remote Key Pressed: BACK`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Set Remote Key Pressed: BACK`); //this.pushBackChanges(); break; } case this.platform.Characteristic.RemoteKey.EXIT: { - this.platform.debug(`${this.accessory.displayName} Set Remote Key Pressed: EXIT`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Set Remote Key Pressed: EXIT`); break; } case this.platform.Characteristic.RemoteKey.PLAY_PAUSE: { - this.platform.debug(`${this.accessory.displayName} Set Remote Key Pressed: PLAY_PAUSE`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Set Remote Key Pressed: PLAY_PAUSE`); break; } case this.platform.Characteristic.RemoteKey.INFORMATION: { - this.platform.debug(`${this.accessory.displayName} Set Remote Key Pressed: INFORMATION`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Set Remote Key Pressed: INFORMATION`); //this.pushMenuChanges(); break; } @@ -190,24 +194,35 @@ export class TV { } private ActiveIdentifierSet(value: CharacteristicValue) { - this.platform.debug(`${this.accessory.displayName} Set Active Identifier: ${value}`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} ActiveIdentifier: ${value}`); + this.ActiveIdentifier = value; } private ActiveSet(value: CharacteristicValue) { - this.platform.debug(`${this.accessory.displayName} Set Active: ${value}`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${value}`); if (!this.device.irtv?.disable_power) { if (value === this.platform.Characteristic.Active.INACTIVE) { this.pushTvOffChanges(); } else { this.pushTvOnChanges(); } - this.ActiveIdentifier = value; - if (this.ActiveIdentifier === undefined) { - this.platform.debug(`IR TV ${this.accessory.displayName} ActiveIdentifier: ${this.ActiveIdentifier}`); - } else { - this.service!.updateCharacteristic(this.platform.Characteristic.Active, this.ActiveIdentifier); - this.platform.device(`Humidifier ${this.accessory.displayName} updateCharacteristic Active: ${this.ActiveIdentifier}`); - } + this.Active = value; + } + } + + private updateHomeKitCharacteristics() { + if (this.Active === undefined) { + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Active: ${this.Active}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.Active, this.Active); + this.platform.device(`${this.device.remoteType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`); + } + if (this.ActiveIdentifier === undefined) { + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} ActiveIdentifier: ${this.ActiveIdentifier}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.ActiveIdentifier, this.ActiveIdentifier); + this.platform.device(`${this.device.remoteType}: ${this.accessory.displayName}` + + ` updateCharacteristic ActiveIdentifier: ${this.ActiveIdentifier}`); } } @@ -227,7 +242,7 @@ export class TV { commandType: 'command', parameter: 'default', command: 'turnOn', - } as any; + } as payload; await this.pushTVChanges(payload); } } @@ -237,7 +252,7 @@ export class TV { commandType: 'command', parameter: 'default', command: 'turnOff', - } as any; + } as payload; await this.pushTVChanges(payload); } @@ -246,7 +261,7 @@ export class TV { commandType: 'command', parameter: 'default', command: 'Ok', - } as any; + } as payload; await this.pushTVChanges(payload); } @@ -255,7 +270,7 @@ export class TV { commandType: 'command', parameter: 'default', command: 'Back', - } as any; + } as payload; await this.pushTVChanges(payload); } @@ -264,7 +279,7 @@ export class TV { commandType: 'command', parameter: 'default', command: 'Menu', - } as any; + } as payload; await this.pushTVChanges(payload); } @@ -273,7 +288,7 @@ export class TV { commandType: 'command', parameter: 'default', command: 'Up', - } as any; + } as payload; await this.pushTVChanges(payload); } @@ -282,7 +297,7 @@ export class TV { commandType: 'command', parameter: 'default', command: 'Down', - } as any; + } as payload; await this.pushTVChanges(payload); } @@ -291,7 +306,7 @@ export class TV { commandType: 'command', parameter: 'default', command: 'Right', - } as any; + } as payload; await this.pushTVChanges(payload); } @@ -300,7 +315,7 @@ export class TV { commandType: 'command', parameter: 'default', command: 'Left', - } as any; + } as payload; await this.pushTVChanges(payload); } @@ -309,7 +324,7 @@ export class TV { commandType: 'command', parameter: 'default', command: 'volumeAdd', - } as any; + } as payload; await this.pushTVChanges(payload); } @@ -318,29 +333,30 @@ export class TV { commandType: 'command', parameter: 'default', command: 'volumeSub', - } as any; + } as payload; await this.pushTVChanges(payload); } - public async pushTVChanges(payload: any) { + public async pushTVChanges(payload: payload) { try { - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`${this.accessory.displayName} pushChanges - ${JSON.stringify(payload)}`); + this.platform.log.info(`${this.device.remoteType}: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); // Make the API request const push = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); - this.platform.debug(`${this.accessory.displayName} Changes pushed - ${push.data}`); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} pushChanges: ${push.data}`); this.statusCode(push); - } catch (e) { + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.platform.log.error(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`${this.device.remoteType}: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } } @@ -349,33 +365,35 @@ export class TV { private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`${this.device.remoteType}: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`${this.device.remoteType}: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`${this.device.remoteType}: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`${this.device.remoteType}: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`${this.device.remoteType}: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`${this.device.remoteType}: ` + + `${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - this.platform.debug('Command successfully sent.'); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`${this.device.remoteType}: ${this.accessory.displayName} Unknown statusCode.`); } } public apiError(e: any) { this.service.updateCharacteristic(this.platform.Characteristic.Active, e); - this.speakerService.updateCharacteristic(this.platform.Characteristic.Active, e); + this.service.updateCharacteristic(this.platform.Characteristic.ActiveIdentifier, e); } } diff --git a/src/irdevices/vacuumcleaners.ts b/src/irdevices/vacuumcleaners.ts index 210c9091..ff70a1b7 100644 --- a/src/irdevices/vacuumcleaners.ts +++ b/src/irdevices/vacuumcleaners.ts @@ -1,7 +1,7 @@ import { AxiosResponse } from 'axios'; import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; -import { irDevicesConfig, DeviceURL, irdevice } from '../settings'; +import { irDevicesConfig, DeviceURL, irdevice, payload } from '../settings'; /** * Platform Accessory @@ -15,6 +15,10 @@ export class VacuumCleaner { // Characteristic Values On!: CharacteristicValue; + // Config + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; + constructor( private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, @@ -46,7 +50,7 @@ export class VacuumCleaner { } private OnSet(value: CharacteristicValue) { - this.platform.debug(`${this.device.remoteType} ${this.accessory.displayName} Set On: ${value}`); + this.platform.debug(`Vacuum Cleaner: ${this.accessory.displayName} On: ${value}`); this.On = value; if (this.On) { this.pushOnChanges(); @@ -55,6 +59,15 @@ export class VacuumCleaner { } } + private updateHomeKitCharacteristics() { + if (this.On === undefined) { + this.platform.debug(`Vacuum Cleaner: ${this.accessory.displayName} On: ${this.On}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.On, this.On); + this.platform.device(`Vacuum Cleaner: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`); + } + } + /** * Pushes the requested changes to the SwitchBot API * deviceType commandType Command command parameter Description @@ -71,7 +84,7 @@ export class VacuumCleaner { commandType: 'command', parameter: 'default', command: 'turnOn', - } as any; + } as payload; await this.pushChanges(payload); } } @@ -82,60 +95,61 @@ export class VacuumCleaner { commandType: 'command', parameter: 'default', command: 'turnOff', - } as any; + } as payload; await this.pushChanges(payload); } } - public async pushChanges(payload: any) { + public async pushChanges(payload: payload) { try { - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`${this.accessory.displayName} pushChanges - ${JSON.stringify(payload)}`); + this.platform.log.info(`Vacuum Cleaner: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); // Make the API request const push = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); - this.platform.debug(`${this.accessory.displayName} Changes pushed - ${push.data}`); + this.platform.debug(`Vacuum Cleaner: ${this.accessory.displayName} pushChanges: ${push.data}`); this.statusCode(push); - } catch (e) { + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.platform.log.error(`Vacuum Cleaner: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Vacuum Cleaner: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Vacuum Cleaner: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } } - - private statusCode(push: AxiosResponse<{ statusCode: number;}>) { + private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`Vacuum Cleaner: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`Vacuum Cleaner: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`Vacuum Cleaner: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`Vacuum Cleaner: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`Vacuum Cleaner: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`Vacuum Cleaner: ${this.accessory.displayName} Device internal error due to device states not synchronized` + + ` with server, Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - this.platform.debug('Command successfully sent.'); + this.platform.debug(`Vacuum Cleaner: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`Vacuum Cleaner: ${this.accessory.displayName} Unknown statusCode.`); } } diff --git a/src/irdevices/waterheaters.ts b/src/irdevices/waterheaters.ts index 00d851e7..b97cb2aa 100644 --- a/src/irdevices/waterheaters.ts +++ b/src/irdevices/waterheaters.ts @@ -1,7 +1,7 @@ import { AxiosResponse } from 'axios'; import { CharacteristicValue, PlatformAccessory, Service } from 'homebridge'; import { SwitchBotPlatform } from '../platform'; -import { irDevicesConfig, DeviceURL, irdevice } from '../settings'; +import { irDevicesConfig, DeviceURL, irdevice, payload } from '../settings'; /** * Platform Accessory @@ -15,6 +15,10 @@ export class WaterHeater { // Characteristic Values Active!: CharacteristicValue; + // Config + private readonly deviceDebug = this.platform.config.options?.debug === 'device' || this.platform.debugMode; + private readonly debugDebug = this.platform.config.options?.debug === 'debug' || this.platform.debugMode; + constructor( private readonly platform: SwitchBotPlatform, private accessory: PlatformAccessory, @@ -55,7 +59,7 @@ export class WaterHeater { } private ActiveSet(value: CharacteristicValue) { - this.platform.debug(`${this.accessory.displayName} Set Active: ${value}`); + this.platform.debug(`Water Heater: ${this.accessory.displayName} Active: ${value}`); if (value === this.platform.Characteristic.Active.INACTIVE) { this.pushWaterHeaterOffChanges(); this.service.setCharacteristic( @@ -67,8 +71,14 @@ export class WaterHeater { this.service.setCharacteristic(this.platform.Characteristic.InUse, this.platform.Characteristic.InUse.IN_USE); } this.Active = value; - if (this.Active !== undefined) { - this.service.updateCharacteristic(this.platform.Characteristic.Active, this.Active); + } + + private updateHomeKitCharacteristics() { + if (this.Active === undefined) { + this.platform.debug(`Water Heater: ${this.accessory.displayName} Active: ${this.Active}`); + } else { + this.service!.updateCharacteristic(this.platform.Characteristic.Active, this.Active); + this.platform.device(`Water Heater: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`); } } @@ -88,7 +98,7 @@ export class WaterHeater { commandType: 'command', parameter: 'default', command: 'turnOn', - } as any; + } as payload; await this.pushChanges(payload); } } @@ -99,66 +109,65 @@ export class WaterHeater { commandType: 'command', parameter: 'default', command: 'turnOff', - } as any; + } as payload; await this.pushChanges(payload); } } - public async pushChanges(payload: any) { + public async pushChanges(payload: payload) { try { - this.platform.log.info( - 'Sending request for', - this.accessory.displayName, - 'to SwitchBot API. command:', - payload.command, - 'parameter:', - payload.parameter, - 'commandType:', - payload.commandType, - ); - this.platform.debug(`${this.accessory.displayName} pushChanges - ${JSON.stringify(payload)}`); + this.platform.log.info(`Water Heater: ${this.accessory.displayName} Sending request to SwitchBot API. command: ${payload.command},` + + ` parameter: ${payload.parameter}, commandType: ${payload.commandType}`); // Make the API request const push = await this.platform.axios.post(`${DeviceURL}/${this.device.deviceId}/commands`, payload); - this.platform.debug(`${this.accessory.displayName} Changes pushed - ${push.data}`); + this.platform.debug(`Water Heater: ${this.accessory.displayName} pushChanges: ${push.data}`); this.statusCode(push); - } catch (e) { + this.updateHomeKitCharacteristics(); + } catch (e: any) { + this.platform.log.error(`Water Heater: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection`); + if (this.deviceDebug) { + this.platform.log.error(`Water Heater: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error Message: ${JSON.stringify(e.message)}`); + } + if (this.debugDebug) { + this.platform.log.error(`Water Heater: ${this.accessory.displayName} failed pushChanges with OpenAPI Connection,` + + ` Error: ${JSON.stringify(e)}`); + } this.apiError(e); } } - - private statusCode(push: AxiosResponse<{ statusCode: number;}>) { + private statusCode(push: AxiosResponse<{ statusCode: number; }>) { switch (push.data.statusCode) { case 151: - this.platform.log.error('Command not supported by this device type.'); + this.platform.log.error(`Water Heater: ${this.accessory.displayName} Command not supported by this device type.`); break; case 152: - this.platform.log.error('Device not found.'); + this.platform.log.error(`Water Heater: ${this.accessory.displayName} Device not found.`); break; case 160: - this.platform.log.error('Command is not supported.'); + this.platform.log.error(`Water Heater: ${this.accessory.displayName} Command is not supported.`); break; case 161: - this.platform.log.error('Device is offline.'); + this.platform.log.error(`Water Heater: ${this.accessory.displayName} Device is offline.`); break; case 171: - this.platform.log.error('Hub Device is offline.'); + this.platform.log.error(`Water Heater: ${this.accessory.displayName} Hub Device is offline.`); break; case 190: - this.platform.log.error('Device internal error due to device states not synchronized with server. Or command fomrat is invalid.'); + this.platform.log.error(`Water Heater: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` + + ` Or command: ${JSON.stringify(push.data)} format is invalid`); break; case 100: - this.platform.debug('Command successfully sent.'); + this.platform.debug(`Water Heater: ${this.accessory.displayName} Command successfully sent.`); break; default: - this.platform.debug('Unknown statusCode.'); + this.platform.debug(`Water Heater: ${this.accessory.displayName} Unknown statusCode.`); } } public apiError(e: any) { - this.service.updateCharacteristic(this.platform.Characteristic.ValveType, e); this.service.updateCharacteristic(this.platform.Characteristic.Active, e); - this.service.updateCharacteristic(this.platform.Characteristic.InUse, e); } } diff --git a/src/platform.ts b/src/platform.ts index 0b0bed02..49b2eb4f 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -47,7 +47,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { // debugMode!: boolean; version = require('../package.json').version; // eslint-disable-line @typescript-eslint/no-var-requires - deviceStatus!: any; + deviceStatus!: deviceResponses; + registeringDevice!: boolean; + debugMode!: boolean; constructor(public readonly log: Logger, public readonly config: SwitchBotPlatformConfig, public readonly api: API) { this.log.debug('Finished initializing platform:', this.config.name); @@ -72,7 +74,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { return; } - //this.debugMode = process.argv.includes('-D') || process.argv.includes('--debug'); + this.debugMode = process.argv.includes('-D') || process.argv.includes('--debug'); // setup axios interceptor to add headers / api key to each request this.axios.interceptors.request.use((request: AxiosRequestConfig) => { @@ -119,26 +121,38 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.config.options = this.config.options || {}; this.config.options.debug; - // Device Config - if (this.config.options.devices) { - for (const deviceConfig of this.config.options.devices!) { - if (!deviceConfig.hide_device && !deviceConfig.deviceType) { - throw new Error('The devices config section is missing the "Device Type" in the config, Check Your Conifg.'); - } - if (!deviceConfig.deviceId) { - throw new Error('The devices config section is missing the "Device ID" in the config, Check Your Conifg.'); + if (this.config.options) { + + // Device Config + if (this.config.options.devices) { + for (const deviceConfig of this.config.options.devices) { + if (!deviceConfig.hide_device) { + if (!deviceConfig.deviceId) { + throw new Error('The devices config section is missing the *Device ID* in the config, Check Your Conifg.'); + } + if (!deviceConfig.configDeviceType && deviceConfig.ble) { + throw new Error('The devices config section is missing the *Device Type* in the config, Check Your Conifg.'); + } + if (deviceConfig.bot) { + if (!deviceConfig.bot?.mode) { + this.log.error('You must set your Bot to Press or Switch Mode'); + } + } + } } } - } - // IR Device Config - if (this.config.options.irdevices) { - for (const irDeviceConfig of this.config.options.irdevices!) { - if (!irDeviceConfig.hide_device && !irDeviceConfig.remoteType) { - throw new Error('The devices config section is missing the "Device Type" in the config, Check Your Conifg.'); - } - if (!irDeviceConfig.deviceId) { - throw new Error('The devices config section is missing the "Device ID" in the config, Check Your Conifg.'); + // IR Device Config + if (this.config.options.irdevices) { + for (const irDeviceConfig of this.config.options.irdevices) { + if (!irDeviceConfig.hide_device) { + if (!irDeviceConfig.deviceId) { + this.log.error('The devices config section is missing the *Device ID* in the config, Check Your Conifg.'); + } + if (!irDeviceConfig.deviceId && !irDeviceConfig.configRemoteType) { + this.log.error('The devices config section is missing the *Device Type* in the config, Check Your Conifg.'); + } + } } } } @@ -160,10 +174,11 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } if (!this.config.credentials) { - throw new Error('Missing Credentials'); + this.debug('Missing Credentials'); } - if (!this.config.credentials.openToken) { - throw new Error('Missing openToken'); + if (!this.config.credentials?.openToken) { + this.log.error('Missing openToken'); + this.log.warn('Cloud Enabled SwitchBot Devices & IR Devices will not work'); } } @@ -172,67 +187,84 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { */ async discoverDevices() { try { - const devicesAPI: any = (await this.axios.get(DeviceURL)).data; - this.deviceListInfo(devicesAPI); - this.debug(JSON.stringify(devicesAPI)); - - // SwitchBot Devices - this.log.info('Total SwitchBot Devices Found:', devicesAPI.body.deviceList.length); - const deviceLists = devicesAPI.body.deviceList; - if (!this.config.options?.devices) { - this.debug(`SwitchBot Device Config Not Set: ${JSON.stringify(this.config.options?.devices)}`); - const devices = deviceLists.map((v: any) => v); - for (const device of devices) { - if (device.deviceType) { - this.createDevice(device); + if (this.config.credentials?.openToken) { + const devicesAPI: any = (await this.axios.get(DeviceURL)).data; + this.deviceListInfo(devicesAPI); + this.debug(JSON.stringify(devicesAPI)); + + // SwitchBot Devices + this.log.info('Total SwitchBot Devices Found:', devicesAPI.body.deviceList.length); + const deviceLists = devicesAPI.body.deviceList; + if (!this.config.options?.devices) { + this.debug(`SwitchBot Device Config Not Set: ${JSON.stringify(this.config.options?.devices)}`); + const devices = deviceLists.map((v: any) => v); + for (const device of devices) { + if (device.deviceType) { + this.createDevice(device); + } + } + } else if (this.config.credentials?.openToken && this.config.options.devices) { + this.debug(`SwitchBot Device Config Set: ${JSON.stringify(this.config.options?.devices)}`); + const deviceConfigs = this.config.options?.devices; + + const mergeBydeviceId = (a1: { deviceId: string; }[], a2: any[]) => + a1.map((itm: { deviceId: string; }) => ({ + ...a2.find((item: { deviceId: string; }) => (item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId) && item), + ...itm, + })); + + const devices = mergeBydeviceId(deviceLists, deviceConfigs); + this.debug(`SwitchBot Devices: ${JSON.stringify(devices)}`); + for (const device of devices) { + if (device.deviceType) { + this.createDevice(device); + } } + } else { + this.log.error('Neither SwitchBot OpenToken or Device Config are not set.'); } - } else { - this.debug(`SwitchBot Device Config Set: ${JSON.stringify(this.config.options?.devices)}`); + // IR Devices + this.log.info('Total IR Devices Found:', devicesAPI.body.infraredRemoteList.length); + const irDeviceLists = devicesAPI.body.infraredRemoteList; + if (!this.config.options?.irdevices) { + this.debug(`IR Device Config Not Set: ${JSON.stringify(this.config.options?.irdevices)}`); + const devices = irDeviceLists.map((v: any) => v); + for (const device of devices) { + if (device.remoteType) { + this.createIRDevice(device); + } + } + } else { + this.debug(`IR Device Config Set: ${JSON.stringify(this.config.options?.irdevices)}`); + const irDeviceConfig = this.config.options?.irdevices; + + const mergeIRBydeviceId = (a1: { deviceId: string; }[], a2: any[]) => + a1.map((itm: { deviceId: string; }) => ({ + ...a2.find((item: { deviceId: string; }) => (item.deviceId === itm.deviceId) && item), + ...itm, + })); + + const devices = mergeIRBydeviceId(irDeviceLists, irDeviceConfig); + this.debug(`IR Devices: ${JSON.stringify(devices)}`); + for (const device of devices) { + if (device.remoteType) { + this.createIRDevice(device); + } + } + } + } else if (!this.config.credentials?.openToken && this.config.options?.devices) { + this.debug(`SwitchBot Device Manual Config Set: ${JSON.stringify(this.config.options?.devices)}`); const deviceConfigs = this.config.options?.devices; - - const mergeBydeviceId = (a1: { deviceId: string; }[], a2: any[]) => - a1.map((itm: { deviceId: string; }) => ({ - ...a2.find((item: { deviceId: string; }) => (item.deviceId.toUpperCase().replace(/[^A-Z0-9]+/g, '') === itm.deviceId) && item), - ...itm, - })); - - const devices = mergeBydeviceId(deviceLists, deviceConfigs); - this.debug(`SwitchBot Devices: ${JSON.stringify(devices)}`); + const devices = deviceConfigs.map((v: any) => v); for (const device of devices) { + device.deviceType = device.configDeviceType; + device.deviceName = device.configDeviceName; if (device.deviceType) { this.createDevice(device); } } - } - // IR Devices - this.log.info('Total IR Devices Found:', devicesAPI.body.infraredRemoteList.length); - const irDeviceLists = devicesAPI.body.infraredRemoteList; - if (!this.config.options?.irdevices) { - this.debug(`IR Device Config Not Set: ${JSON.stringify(this.config.options?.irdevices)}`); - const devices = irDeviceLists.map((v: any) => v); - for (const device of devices) { - if (device.remoteType) { - this.createIRDevice(device); - } - } } else { - this.debug(`IR Device Config Set: ${JSON.stringify(this.config.options?.irdevices)}`); - const irDeviceConfig = this.config.options?.irdevices; - - const mergeIRBydeviceId = (a1: { deviceId: string; }[], a2: any[]) => - a1.map((itm: { deviceId: string; }) => ({ - ...a2.find((item: { deviceId: string; }) => (item.deviceId === itm.deviceId) && item), - ...itm, - })); - - const devices = mergeIRBydeviceId(irDeviceLists, irDeviceConfig); - this.debug(`IR Devices: ${JSON.stringify(devices)}`); - for (const device of devices) { - if (device.remoteType) { - this.createIRDevice(device); - } - } + this.log.error('Neither SwitchBot OpenToken or Device Config are not set.'); } } catch (e: any) { this.log.error('Failed to Discover Devices.', JSON.stringify(e.message)); @@ -355,6 +387,23 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } + private registerDevice(device: device & devicesConfig) { + if (!device.hide_device && device.enableCloudService && device.ble) { + this.registeringDevice = true; + this.device(`Device: ${device.deviceName} Both OpenAPI and BLE Connections Enabled`); + } else if (!device.hide_device && device.deviceId && device.configDeviceType && device.configDeviceName && !device.enableCloudService) { + this.registeringDevice = true; + this.device(`Device: ${device.deviceName} BLE Connection Enabled`); + } else if (!device.hide_device && device.enableCloudService && !device.ble) { + this.registeringDevice = true; + this.device(`Device: ${device.deviceName} OpenAPI Connection Enabled`); + } else { + this.registeringDevice = false; + this.device(`Device: ${device.deviceName} Neither OpenAPI and BLE Enabled`); + } + return this.registeringDevice; + } + private async createHumidifier(device: device & devicesConfig) { const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.deviceType}`); @@ -364,7 +413,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { if (existingAccessory) { // the accessory already exists - if (!device.hide_device && device.enableCloudService) { + if (this.registerDevice(device)) { this.log.info(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.: @@ -382,7 +431,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } else { this.unregisterPlatformAccessories(existingAccessory); } - } else if (!device.hide_device && device.enableCloudService) { + } else if (this.registerDevice(device)) { // the accessory does not yet exist, so we need to create it this.log.info(`Adding new accessory: ${device.deviceName} ${device.deviceType} DeviceID: ${device.deviceId}`); @@ -418,13 +467,10 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { if (existingAccessory) { // the accessory already exists - if (!device.hide_device && device.enableCloudService) { + if (this.registerDevice(device)) { this.log.info(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); this.debug(JSON.stringify(device.bot?.mode)); - if (!device.bot?.mode) { - this.log.error('You must set your Bot to Press or Switch Mode'); - } // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.: existingAccessory.context.model = device.deviceType; @@ -440,14 +486,12 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } else { this.unregisterPlatformAccessories(existingAccessory); } - } else if (!device.hide_device && device.enableCloudService) { + } else if (this.registerDevice(device)) { // the accessory does not yet exist, so we need to create it this.log.info(`Adding new accessory: ${device.deviceName} ${device.deviceType} DeviceID: ${device.deviceId}`); this.debug(JSON.stringify(device.bot?.mode)); - if (!device.bot?.mode) { - this.log.error('You must set your Bot to Press or Switch Mode'); - } + // create a new accessory const accessory = new this.api.platformAccessory(device.deviceName, uuid); @@ -481,7 +525,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { if (existingAccessory) { // the accessory already exists - if (!device.hide_device && device.enableCloudService) { + if (this.registerDevice(device)) { this.log.info(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.: @@ -498,7 +542,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } else { this.unregisterPlatformAccessories(existingAccessory); } - } else if (!device.hide_device && device.enableCloudService) { + } else if (this.registerDevice(device)) { // the accessory does not yet exist, so we need to create it this.log.info(`Adding new accessory: ${device.deviceName} ${device.deviceType} DeviceID: ${device.deviceId}`); @@ -534,7 +578,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { if (existingAccessory) { // the accessory already exists - if (!device.hide_device && device.enableCloudService) { + if (this.registerDevice(device)) { this.log.info(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.: @@ -550,7 +594,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } else { this.unregisterPlatformAccessories(existingAccessory); } - } else if (!device.hide_device && device.enableCloudService) { + } else if (this.registerDevice(device)) { // the accessory does not yet exist, so we need to create it this.log.info(`Adding new accessory: ${device.deviceName} ${device.deviceType} DeviceID: ${device.deviceId}`); @@ -585,7 +629,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { if (existingAccessory) { // the accessory already exists - if (!device.hide_device && device.enableCloudService!) { + if (this.registerDevice(device)) { this.log.info(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.deviceId}`); // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.: @@ -601,7 +645,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } else { this.unregisterPlatformAccessories(existingAccessory); } - } else if (!device.hide_device && device.enableCloudService!) { + } else if (this.registerDevice(device)) { // the accessory does not yet exist, so we need to create it this.log.info(`Adding new accessory: ${device.deviceName} ${device.deviceType} DeviceID: ${device.deviceId}`); @@ -696,10 +740,10 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { if (device.group && !device.curtain?.disable_group) { this.debug(`[Curtain Config] disable_group: ${device.curtain?.disable_group}`); - return device.master && !device.hide_device && device.enableCloudService; + return device.master && this.registerDevice(device); } else { this.debug(`[Curtain Config] disable_group: ${device.curtain?.disable_group}, UnGrouping ${device.master}`); - return !device.hide_device && device.enableCloudService; + return this.registerDevice(device); } } diff --git a/src/settings.ts b/src/settings.ts index c545bf70..be9f615c 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -38,7 +38,8 @@ export type options = { }; export interface devicesConfig extends device { - deviceType: string; + configDeviceType: string; + configDeviceName?: string; deviceId: string; bot?: bot; meter?: meter; @@ -69,6 +70,8 @@ export type humidifier = { export type curtain = { disable_group?: boolean; hide_lightsensor?: boolean; + set_minLux?: number; + set_maxLux?: number; refreshRate?: number; set_max?: number; set_min?: number; @@ -85,7 +88,7 @@ export type colorbulb = { }; export interface irDevicesConfig extends irdevice { - type?: string; + configRemoteType?: string; deviceId: string; irfan?: irfan; irair?: irair; @@ -134,6 +137,12 @@ export type other = { commandOff?: string; }; +export type payload = { + commandType: string; + parameter: string; + command: string; +}; + export interface AxiosRequestConfig { params?: Record; headers?: any; @@ -306,4 +315,560 @@ export type switchbot = { wait: ( arg0: number ) => any; -}; \ No newline at end of file +}; + +export function rgb2hs(r, g, b) { + /* + Credit: + https://github.com/WickyNilliams/pure-color + */ + r = parseInt(r); + g = parseInt(g); + b = parseInt(b); + const min = Math.min(r, g, b); + const max = Math.max(r, g, b); + const delta = max - min; + let h; + let s; + if (max === 0) { + s = 0; + } else { + s = (delta / max) * 100; + } + if (max === min) { + h = 0; + } else if (r === max) { + h = (g - b) / delta; + } else if (g === max) { + h = 2 + (b - r) / delta; + } else if (b === max) { + h = 4 + (r - g) / delta; + } + h = Math.min(h * 60, 360); + + if (h < 0) { + h += 360; + } + return [Math.round(h), Math.round(s)]; +} + +export function hs2rgb(h, s) { + /* + Credit: + https://github.com/WickyNilliams/pure-color + */ + h = parseInt(h) / 60; + s = parseInt(s) / 100; + const f = h - Math.floor(h); + const p = 255 * (1 - s); + const q = 255 * (1 - s * f); + const t = 255 * (1 - s * (1 - f)); + let rgb; + switch (Math.floor(h) % 6) { + case 0: + rgb = [255, t, p]; + break; + case 1: + rgb = [q, 255, p]; + break; + case 2: + rgb = [p, 255, t]; + break; + case 3: + rgb = [p, q, 255]; + break; + case 4: + rgb = [t, p, 255]; + break; + case 5: + rgb = [255, p, q]; + break; + } + if (rgb[0] === 255) { + rgb[1] *= 0.8; + rgb[2] *= 0.8; + if (rgb[1] <= 25 && rgb[2] <= 25) { + rgb[1] = 0; + rgb[2] = 0; + } + } + return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2])]; +} + +export function k2rgb(k) { + // Set kelvin to nearest 100, between 2000 and 7100 + k = Math.round(k / 100) * 100; + k = Math.max(Math.min(k, 7100), 2000); + + // k should now appear in our table of kelvin to rgb + const values = { + 2000: [255, 141, 11], + 2100: [255, 146, 29], + 2200: [255, 147, 44], + 2300: [255, 152, 54], + 2400: [255, 157, 63], + 2500: [255, 166, 69], + 2600: [255, 170, 77], + 2700: [255, 174, 84], + 2800: [255, 173, 94], + 2900: [255, 177, 101], + 3000: [255, 180, 107], + 3100: [255, 189, 111], + 3200: [255, 187, 120], + 3300: [255, 195, 124], + 3400: [255, 198, 130], + 3500: [255, 201, 135], + 3600: [255, 203, 141], + 3700: [255, 206, 146], + 3800: [255, 204, 153], + 3900: [255, 206, 159], + 4000: [255, 213, 161], + 4100: [255, 215, 166], + 4200: [255, 217, 171], + 4300: [255, 219, 175], + 4400: [255, 221, 180], + 4500: [255, 223, 184], + 4600: [255, 225, 188], + 4700: [255, 226, 192], + 4800: [255, 228, 196], + 4900: [255, 229, 200], + 5000: [255, 231, 204], + 5100: [255, 230, 210], + 5200: [255, 234, 211], + 5300: [255, 235, 215], + 5400: [255, 237, 218], + 5500: [255, 236, 224], + 5700: [255, 240, 228], + 5800: [255, 241, 231], + 5900: [255, 243, 234], + 6000: [255, 244, 237], + 6100: [255, 245, 240], + 6200: [255, 246, 243], + 6300: [255, 247, 247], + 6400: [255, 248, 251], + 6500: [255, 249, 253], + 6600: [254, 249, 255], + 6700: [252, 247, 255], + 6800: [249, 246, 255], + 6900: [247, 245, 255], + 7000: [245, 243, 255], + 7100: [243, 242, 255], + }; + + // Return the value + return values[k]; +} + +export function m2hs(m) { + /* + Credit: + https://github.com/homebridge/HAP-NodeJS + */ + const table = { + 100: [19, 222.1], + 101: [18.7, 222.2], + 102: [18.4, 222.3], + 103: [18.2, 222.3], + 104: [17.9, 222.4], + 105: [17.6, 222.5], + 106: [17.3, 222.7], + 107: [17, 222.8], + 108: [16.7, 222.9], + 109: [16.4, 223], + 110: [16.1, 223.2], + 111: [15.8, 223.3], + 112: [15.4, 223.4], + 113: [15.2, 223.6], + 114: [14.9, 223.8], + 115: [14.7, 223.9], + 116: [14.3, 224.1], + 117: [14.1, 224.2], + 118: [13.8, 224.4], + 119: [13.5, 224.6], + 120: [13.2, 224.8], + 121: [12.9, 225], + 122: [12.5, 225.3], + 123: [12.2, 225.6], + 124: [11.8, 225.9], + 125: [11.4, 226.3], + 126: [11.1, 226.7], + 127: [10.7, 227.1], + 128: [10.3, 227.6], + 129: [9.9, 228], + 130: [9.6, 228.5], + 131: [9.3, 229.1], + 132: [8.9, 229.6], + 133: [8.5, 230.2], + 134: [8.2, 230.9], + 135: [7.8, 231.6], + 136: [7.5, 232.5], + 137: [7.1, 233.5], + 138: [6.7, 234.6], + 139: [6.3, 235.8], + 140: [6, 237.1], + 141: [5.6, 238.9], + 142: [5.2, 240.9], + 143: [5, 242.9], + 144: [4.8, 244.9], + 145: [4.6, 246.9], + 146: [4.4, 249.3], + 147: [4.3, 251.9], + 148: [4.1, 254.9], + 149: [3.9, 258], + 150: [3.7, 261.8], + 151: [3.4, 265.9], + 152: [3.2, 271], + 153: [3, 276.4], + 154: [2.8, 283.6], + 155: [2.6, 290.4], + 156: [2.3, 295.3], + 157: [2.1, 300], + 158: [1.9, 300], + 159: [1.6, 300], + 160: [1.4, 195.8], + 161: [1.2, 84.3], + 162: [1.3, 58.2], + 163: [1.5, 55.9], + 164: [1.7, 53.2], + 165: [1.9, 50.2], + 166: [2.1, 47.1], + 167: [2.4, 44.5], + 168: [2.6, 42.6], + 169: [2.9, 40.9], + 170: [3.1, 39.5], + 171: [3.4, 38.3], + 172: [3.7, 37.3], + 173: [3.9, 36.5], + 174: [4.2, 35.7], + 175: [4.4, 35.1], + 176: [4.6, 34.5], + 177: [4.9, 34], + 178: [5.1, 33.5], + 179: [5.3, 33], + 180: [5.6, 32.7], + 181: [5.8, 32.3], + 182: [6, 32], + 183: [6.3, 31.7], + 184: [6.5, 31.4], + 185: [6.7, 31.2], + 186: [7, 30.9], + 187: [7.2, 30.7], + 188: [7.4, 30.5], + 189: [7.6, 30.3], + 190: [7.9, 30.1], + 191: [8.1, 29.9], + 192: [8.4, 29.7], + 193: [8.6, 29.6], + 194: [8.9, 29.5], + 195: [9.1, 29.3], + 196: [9.4, 29.2], + 197: [9.6, 29.1], + 198: [9.8, 29], + 199: [10, 28.9], + 200: [10.2, 28.7], + 201: [10.5, 28.7], + 202: [10.7, 28.6], + 203: [11, 28.5], + 204: [11.2, 28.4], + 205: [11.4, 28.3], + 206: [11.6, 28.3], + 207: [11.8, 28.2], + 208: [12.1, 28.1], + 209: [12.3, 28.1], + 210: [12.5, 28], + 211: [12.7, 28], + 212: [12.9, 27.9], + 213: [13.2, 27.8], + 214: [13.4, 27.8], + 215: [13.6, 27.7], + 216: [13.8, 27.7], + 217: [14, 27.7], + 218: [14.3, 27.6], + 219: [14.5, 27.6], + 220: [14.7, 27.5], + 221: [14.9, 27.5], + 222: [15.1, 27.5], + 223: [15.3, 27.4], + 224: [15.5, 27.4], + 225: [15.8, 27.4], + 226: [16, 27.3], + 227: [16.2, 27.3], + 228: [16.4, 27.3], + 229: [16.6, 27.3], + 230: [16.8, 27.2], + 231: [17, 27.2], + 232: [17.2, 27.2], + 233: [17.4, 27.2], + 234: [17.6, 27.2], + 235: [17.8, 27.1], + 236: [18, 27.1], + 237: [18.2, 27.1], + 238: [18.4, 27.1], + 239: [18.7, 27.1], + 240: [18.8, 27], + 241: [19, 27], + 242: [19.2, 27], + 243: [19.4, 27], + 244: [19.6, 27], + 245: [19.8, 27], + 246: [20, 27], + 247: [20.3, 26.9], + 248: [20.5, 26.9], + 249: [20.6, 26.9], + 250: [20.8, 26.9], + 251: [21, 26.9], + 252: [21.3, 26.9], + 253: [21.5, 26.9], + 254: [21.6, 26.9], + 255: [21.8, 26.8], + 256: [22, 26.8], + 257: [22.2, 26.8], + 258: [22.4, 26.8], + 259: [22.6, 26.8], + 260: [22.8, 26.8], + 261: [23, 26.8], + 262: [23.2, 26.8], + 263: [23.4, 26.8], + 264: [23.6, 26.8], + 265: [23.8, 26.8], + 266: [24, 26.8], + 267: [24.1, 26.8], + 268: [24.3, 26.8], + 269: [24.5, 26.8], + 270: [24.7, 26.8], + 271: [24.8, 26.8], + 272: [25.1, 26.7], + 273: [25.3, 26.7], + 274: [25.4, 26.7], + 275: [25.6, 26.7], + 276: [25.8, 26.7], + 277: [26, 26.7], + 278: [26.1, 26.7], + 279: [26.3, 26.7], + 280: [26.5, 26.7], + 281: [26.7, 26.7], + 282: [26.9, 26.7], + 283: [27.1, 26.7], + 284: [27.3, 26.7], + 285: [27.5, 26.7], + 286: [27.7, 26.7], + 287: [27.8, 26.7], + 288: [28, 26.7], + 289: [28.2, 26.7], + 290: [28.4, 26.7], + 291: [28.6, 26.7], + 292: [28.8, 26.7], + 293: [28.9, 26.7], + 294: [29.1, 26.7], + 295: [29.3, 26.7], + 296: [29.5, 26.7], + 297: [29.6, 26.7], + 298: [29.8, 26.7], + 299: [30, 26.7], + 300: [30.2, 26.7], + 301: [30.4, 26.7], + 302: [30.5, 26.7], + 303: [30.7, 26.7], + 304: [30.9, 26.7], + 305: [31.1, 26.7], + 306: [31.2, 26.7], + 307: [31.4, 26.7], + 308: [31.6, 26.7], + 309: [31.8, 26.8], + 310: [31.9, 26.8], + 311: [32.1, 26.8], + 312: [32.3, 26.8], + 313: [32.5, 26.8], + 314: [32.6, 26.8], + 315: [32.8, 26.8], + 316: [33, 26.8], + 317: [33.2, 26.8], + 318: [33.3, 26.8], + 319: [33.5, 26.8], + 320: [33.7, 26.8], + 321: [33.8, 26.8], + 322: [34, 26.8], + 323: [34.2, 26.8], + 324: [34.4, 26.8], + 325: [34.5, 26.8], + 326: [34.7, 26.8], + 327: [34.9, 26.8], + 328: [35.1, 26.8], + 329: [35.2, 26.8], + 330: [35.4, 26.8], + 331: [35.5, 26.8], + 332: [35.7, 26.8], + 333: [35.9, 26.8], + 334: [36.1, 26.8], + 335: [36.3, 26.9], + 336: [36.5, 26.9], + 337: [36.7, 26.9], + 338: [36.9, 26.9], + 339: [37.1, 26.9], + 340: [37.2, 26.9], + 341: [37.4, 26.9], + 342: [37.5, 26.9], + 343: [37.7, 26.9], + 344: [37.9, 26.9], + 345: [38.1, 26.9], + 346: [38.3, 26.9], + 347: [38.5, 26.9], + 348: [38.7, 26.9], + 349: [38.9, 26.9], + 350: [39, 26.9], + 351: [39.2, 26.9], + 352: [39.3, 27], + 353: [39.5, 27], + 354: [39.7, 27], + 355: [39.9, 27], + 356: [40.1, 27], + 357: [40.2, 27], + 358: [40.4, 27], + 359: [40.6, 27], + 360: [40.8, 27], + 361: [40.9, 27], + 362: [41.1, 27], + 363: [41.2, 27], + 364: [41.4, 27], + 365: [41.6, 27], + 366: [41.8, 27], + 367: [42, 27], + 368: [42.1, 27.1], + 369: [42.3, 27.1], + 370: [42.4, 27.1], + 371: [42.6, 27.1], + 372: [42.8, 27.1], + 373: [43, 27.1], + 374: [43.1, 27.1], + 375: [43.2, 27.1], + 376: [43.4, 27.1], + 377: [43.6, 27.1], + 378: [43.8, 27.1], + 379: [43.9, 27.1], + 380: [44.1, 27.1], + 381: [44.3, 27.2], + 382: [44.4, 27.2], + 383: [44.6, 27.2], + 384: [44.7, 27.2], + 385: [44.9, 27.2], + 386: [45.1, 27.2], + 387: [45.3, 27.2], + 388: [45.5, 27.2], + 389: [45.6, 27.2], + 390: [45.8, 27.2], + 391: [46, 27.2], + 392: [46.2, 27.2], + 393: [46.4, 27.3], + 394: [46.5, 27.3], + 395: [46.7, 27.3], + 396: [46.9, 27.3], + 397: [47.1, 27.3], + 398: [47.2, 27.3], + 399: [47.4, 27.3], + 400: [47.6, 27.3], + 401: [47.7, 27.3], + 402: [47.9, 27.3], + 403: [48.1, 27.3], + 404: [48.3, 27.3], + 405: [48.5, 27.4], + 406: [48.7, 27.4], + 407: [48.8, 27.4], + 408: [49, 27.4], + 409: [49.2, 27.4], + 410: [49.4, 27.4], + 411: [49.6, 27.4], + 412: [49.7, 27.4], + 413: [49.9, 27.4], + 414: [50.1, 27.4], + 415: [50.2, 27.4], + 416: [50.4, 27.4], + 417: [50.6, 27.5], + 418: [50.7, 27.5], + 419: [50.9, 27.5], + 420: [51.1, 27.5], + 421: [51.2, 27.5], + 422: [51.4, 27.5], + 423: [51.6, 27.5], + 424: [51.7, 27.5], + 425: [51.9, 27.5], + 426: [52.1, 27.5], + 427: [51.2, 27.6], + 428: [52.4, 27.6], + 429: [52.5, 27.6], + 430: [52.7, 27.6], + 431: [52.9, 27.6], + 432: [53.1, 27.6], + 433: [53.2, 27.6], + 434: [53.4, 27.6], + 435: [53.6, 27.6], + 436: [53.7, 27.6], + 437: [53.9, 27.6], + 438: [54.1, 27.7], + 439: [54.2, 27.7], + 440: [54.3, 27.7], + 441: [54.5, 27.7], + 442: [54.7, 27.7], + 443: [54.8, 27.7], + 444: [55, 27.7], + 445: [55.2, 27.7], + 446: [55.3, 27.7], + 447: [55.5, 27.7], + 448: [55.7, 27.7], + 449: [55.8, 27.8], + 450: [56, 27.8], + 451: [56.2, 27.8], + 452: [56.3, 27.8], + 453: [56.5, 27.8], + 454: [56.7, 27.8], + 455: [56.8, 27.8], + 456: [57, 27.8], + 457: [57.2, 27.8], + 458: [57.3, 27.9], + 459: [57.4, 27.9], + 460: [57.6, 27.9], + 461: [57.8, 27.9], + 462: [57.9, 27.9], + 463: [58.1, 27.9], + 464: [58.3, 27.9], + 465: [58.4, 27.9], + 466: [58.6, 27.9], + 467: [58.8, 27.9], + 468: [59, 28], + 469: [59.1, 28], + 470: [59.2, 28], + 471: [59.4, 28], + 472: [59.6, 28], + 473: [59.7, 28], + 474: [60, 28], + 475: [60.1, 28], + 476: [60.2, 28], + 477: [60.4, 28], + 478: [60.6, 28.1], + 479: [60.7, 28.1], + 480: [60.9, 28.1], + 481: [60.1, 28.1], + 482: [60.3, 28.1], + 483: [61.4, 28.1], + 484: [61.5, 28.1], + 485: [61.7, 28.1], + 486: [61.9, 28.1], + 487: [62, 28.2], + 488: [62.2, 28.2], + 489: [62.3, 28.2], + 490: [62.5, 28.2], + 491: [62.7, 28.2], + 492: [62.8, 28.2], + 493: [63, 28.2], + 494: [63.2, 28.2], + 495: [63.3, 28.2], + 496: [63.4, 28.2], + 497: [63.6, 28.2], + 498: [63.8, 28.3], + 499: [63.9, 28.3], + 500: [64.1, 28.3], + }; + const input = Math.min(Math.max(Math.round(m), 140), 500); + const toReturn = table[input]; + return [Math.round(toReturn[1]), Math.round(toReturn[0])]; + +} \ No newline at end of file diff --git a/switchbot/contactsensors.json b/switchbot/contactsensors.json deleted file mode 100644 index aeb4d621..00000000 --- a/switchbot/contactsensors.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "statusCode": 100, - "body": { - "deviceId": "A11111AAA11A", - "deviceType": "Contact Sensor", - "hubDeviceId": "A11111AAA11A", - "moveDetected": false, - "brightness": "bright", - "openState": "close" - }, - "message": "success" -} \ No newline at end of file diff --git a/switchbot/motionsensors.json b/switchbot/motionsensors.json deleted file mode 100644 index 66998b08..00000000 --- a/switchbot/motionsensors.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "statusCode": 100, - "body": { - "deviceId": "A11111AAA11A", - "deviceType": "Motion Sensor", - "hubDeviceId": "A11111AAA11A", - "moveDetected": true, - "brightness": "bright" - }, - "message": "success" -} \ No newline at end of file