diff --git a/.github/workflows/beta-release.yml b/.github/workflows/beta-release.yml index bf65b766..b9ad735a 100644 --- a/.github/workflows/beta-release.yml +++ b/.github/workflows/beta-release.yml @@ -3,8 +3,6 @@ name: Node-CI Beta on: push: branches: [beta-*.*.*, beta] - release: - types: [prereleased] workflow_dispatch: jobs: @@ -14,15 +12,14 @@ jobs: enable_coverage: true secrets: token: ${{ secrets.GITHUB_TOKEN }} + lint: needs: build_and_test uses: OpenWonderLabs/.github/.github/workflows/eslint.yml@latest publish: needs: lint - if: ${{ github.repository == 'OpenWonderLabs/homebridge-switchbot' }} - uses: OpenWonderLabs/.github/.github/workflows/npm-publish.yml@latest with: tag: 'beta' @@ -32,6 +29,17 @@ jobs: secrets: npm_auth_token: ${{ secrets.npm_token }} + pre-release: + needs: publish + if: ${{ github.repository == 'OpenWonderLabs/homebridge-switchbot' }} + uses: OpenWonderLabs/.github/.github/workflows/pre-release.yml@latest + with: + npm_version: ${{ needs.publish.outputs.NPM_VERSION }} + body: | + **Beta Release** + **Version**: v${{ needs.publish.outputs.NPM_VERSION }} + [How To Test Beta Releases](https://github.com/OpenWonderLabs/homebridge-switchbot/wiki/Beta-Version) + github-releases-to-discord: name: Discord Webhooks needs: [build_and_test,publish] diff --git a/CHANGELOG.md b/CHANGELOG.md index fc333f8d..d86f8990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,32 @@ All notable changes to this project will be documented in this file. This project uses [Semantic Versioning](https://semver.org/) +## [4.0.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v4.0.0) (2024-10-11) + +### What's Changed +#### ⚠️ Breaking Changes +- Added `deviceType` based config + - This allows you to set a setting that will be used across the same deviceType + - `deviceType` based config settings can be over written by individual device settings set by the deviceId +- Added `remoteType` based config + - This allows you to set a setting that will be used across the same IR remoteType + - `remoteType` based config settings can be over written by individual device settings set by the deviceId +- All device specific configs have been moved to the same level on the config to elimate the use of multiple of the same config and to better support the new `deviceType` based config. + - ** ⚠️ If you have config set for any of the following SwitchBot device Types: `Curtain`, `Blind Tilt`, `Color Bulb`, `Strip Light`, `Hub`, `Meter`, `Meter Plus`, `Humidifier`, `Contact Sensor`, `Motion Sensor`, `Water Detector`, `Ceiling Light`, or `Lock`; you will need to re-setup your config for that deviceId** + - ** ⚠️ If you have config set for any of the following IR remote Types: `Fan`, `DIY Fan`, `Light`, `DIY Light`, `Air Conditioner`, `DIY Air Conditioner`, or `Others`; you will need to re-setup your config for that deviceId** +- OpenAPI is now support by Version [3.0.0](https://github.com/OpenWonderLabs/node-switchbot/releases/latest) of [`node-switchbot`](https://github.com/OpenWonderLabs/node-switchbot), so made changes to refect that. + +#### Other Changes +- Add `K10+ Pro` deviceType Support +- Housekeeping and updated dependencies. + +**Full Changelog**: https://github.com/OpenWonderLabs/homebridge-switchbot/compare/v3.8.3...v4.0.0 + ## [3.8.3](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v3.8.3) (2024-10-09) ### What's Changed - Fix incorrect handling of openState in Contact Sensor [#1061](https://github.com/OpenWonderLabs/homebridge-switchbot/pull/1061), Thanks [@nzws](https://github.com/nzws) -- Fix light level stuck at ma [#1066](https://github.com/OpenWonderLabs/homebridge-switchbot/pull/1066), Thanks [@4gra](https://github.com/4gra) +- Fix light level stuck at max [#1066](https://github.com/OpenWonderLabs/homebridge-switchbot/pull/1066), Thanks [@4gra](https://github.com/4gra) - Housekeeping and updated dependencies. **Full Changelog**: https://github.com/OpenWonderLabs/homebridge-switchbot/compare/v3.8.2...v3.8.3 diff --git a/config.schema.json b/config.schema.json index 50dfd1f3..5d783864 100644 --- a/config.schema.json +++ b/config.schema.json @@ -52,11 +52,6 @@ "title": "Devices", "type": "object", "properties": { - "deviceId": { - "title": "Device ID", - "type": "string", - "placeholder": "81F3UT59513F" - }, "configDeviceName": { "title": "Device Name", "type": "string", @@ -65,6 +60,11 @@ "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId);" } }, + "deviceId": { + "title": "Device ID", + "type": "string", + "placeholder": "81F3UT59513F" + }, "hide_device": { "title": "Hide Device", "type": "boolean", @@ -76,6 +76,9 @@ "configDeviceType": { "title": "Device Type", "type": "string", + "condition": { + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device);" + }, "oneOf": [ { "title": "Blind Tilt", @@ -179,6 +182,18 @@ "MeterPlus" ] }, + { + "title": "K10+", + "enum": [ + "K10+" + ] + }, + { + "title": "K10+ Pro", + "enum": [ + "K10+ Pro" + ] + }, { "title": "Mini Robot Vacuum K10+", "enum": [ @@ -221,10 +236,16 @@ "Robot Vacuum Cleaner S1 Plus" ] }, + { + "title": "Robot Vacuum Cleaner S10", + "enum": [ + "Robot Vacuum Cleaner S10" + ] + }, { "title": "Roller Shade", "enum": [ - "Roller Shade" + "WoRollerShade" ] }, { @@ -251,14 +272,15 @@ "Strip Light" ] } - ], - "condition": { - "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device);" - } + ] }, "connectionType": { "title": "Connection Type", "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device);" + }, "oneOf": [ { "title": "BLE", @@ -284,17 +306,14 @@ "OpenAPI" ] } - ], - "description": "Bluetooth (BLE) API is only available for the following Device Types: Humidifier, Meter, MeterPlus, Curtain, Bot, Motion Sensor, Contact Sensor, Plug Mini (US), Plug Mini (JP), & Color Bulb", - "condition": { - "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device);" - } + ] }, - "webhook": { - "title": "Enable Webhook", - "type": "boolean", + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, "condition": { - "functionBody": "return (model.options && model.options.devices && model.options.webhookURL && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].connectionType === 'OpenAPI' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI'));" + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].connectionType === 'BLE' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI'));" } }, "customBLEaddress": { @@ -305,1434 +324,11466 @@ "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].connectionType === 'BLE' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI'));" } }, - "scanDuration": { - "title": "Scan Duration (Default is 1 Second) for BLE", - "type": "number", - "placeholder": 1, + "webhook": { + "title": "Enable Webhook", + "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].connectionType === 'BLE' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI'));" + "functionBody": "return (model.options && model.options.devices && model.options.webhookURL && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].connectionType === 'OpenAPI' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI'));" } }, - "hub": { - "type": "object", - "properties": { - "hide_temperature": { - "title": "Hide Hub 2's Temperature Sensor", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Hub 2' && model.options.devices[arrayIndices].deviceId);" - } - }, - "convertUnitTo": { - "title": "Convert Hub's Temperature Unit To", - "type": "string", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Hub 2' && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hub.hide_temperature);" - }, - "oneOf": [ - { - "title": "Celsius", - "enum": [ - "CELSIUS" - ] - }, - { - "title": "Fahrenheit", - "enum": [ - "FAHRENHEIT" - ] - } - ] - }, - "hide_humidity": { - "title": "Hide Hub 2's Humidity Sensor", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Hub 2') && model.options.devices[arrayIndices].deviceId);" - } - }, - "hide_lightsensor": { - "title": "Hide Hub 2's Light Sensor", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Hub 2') && model.options.devices[arrayIndices].deviceId);" - } - } - } - }, - "bot": { - "type": "object", - "properties": { - "mode": { - "title": "Mode", - "type": "string", - "required": true, - "default": "", - "oneOf": [ - { - "title": "Default", - "enum": [ - "" - ] - }, - { - "title": "Multi-Press", - "enum": [ - "multipress" - ] - }, - { - "title": "Press", - "enum": [ - "press" - ] - }, - { - "title": "Switch", - "enum": [ - "switch" - ] - } - ], - "description": "Press Mode presses one time per 'On' state in the Home App/API. Switch Mode allows you to set how many presses occur when the device is set to 'On'. Multi-Press allows you to queue up multiple button presses. It creates a queue by counting each time the device is set to 'On'. It is most useful for scripting with cURL requests or other API calls. Multi-Press only works when using OpenAPI.", - "condition": { - "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, - "default": "", - "oneOf": [ - { - "title": "Default", - "enum": [ - "" - ] - }, - { - "title": "Door", - "enum": [ - "door" - ] - }, - { - "title": "Fan", - "enum": [ - "fan" - ] - }, - { - "title": "Faucet", - "enum": [ - "faucet" - ] - }, - { - "title": "Garage Door", - "enum": [ - "garagedoor" - ] - }, - { - "title": "Lock", - "enum": [ - "lock" - ] - }, - { - "title": "Outlet", - "enum": [ - "outlet" - ] - }, - { - "title": "Stateful Programmable Switch (Only Works in 3rd Party Home Apps)", - "enum": [ - "stateful" - ] - }, - { - "title": "Switch", - "enum": [ - "switch" - ] - }, - { - "title": "Window", - "enum": [ - "window" - ] - }, - { - "title": "Window Covering", - "enum": [ - "windowcovering" - ] - } - ], - "placeholder": "outlet", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Bot' && model.options.devices[arrayIndices].deviceId);" - } - }, - "allowPush": { - "title": "Allow Changes to Be Pushed", - "description": "If set to true, this will allow commands to be sent even if device state is already in state that is being pushed.", - "type": "boolean", - "condition": { - "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);" - } + "type": { + "title": "What Type of Device do you want to display in the Home App?", + "type": "string", + "required": true, + "default": "", + "placeholder": "outlet", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Bot' && model.options.devices[arrayIndices].deviceId);" + }, + "oneOf": [ + { + "title": "Default", + "enum": [ + "" + ] }, - "doublePress": { - "title": "How many presses do you want to simulate?", - "type": "number", - "placeholder": "2", - "condition": { - "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);" - } - }, - "pushRatePress": { - "title": "Double Press Push Rate", - "type": "number", - "placeholder": "15", - "description": "Indicates the number of seconds between pushes for double presses to SwitchBot API", - "condition": { - "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 && model.options.devices[arrayIndices].bot.doublePress > 1);" - } - } - } - }, - "meter": { - "type": "object", - "properties": { - "hide_temperature": { - "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].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)') && model.options.devices[arrayIndices].deviceId);" - } + { + "title": "Door", + "enum": [ + "door" + ] }, - "convertUnitTo": { - "title": "Convert Meter's Temperature Unit To", - "type": "string", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)') && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].meter.hide_temperature);" - }, - "oneOf": [ - { - "title": "Celsius", - "enum": [ - "CELSIUS" - ] - }, - { - "title": "Fahrenheit", - "enum": [ - "FAHRENHEIT" - ] - } + { + "title": "Fan", + "enum": [ + "fan" ] }, - "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].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)') && model.options.devices[arrayIndices].deviceId);" - } - } - } - }, - "iosensor": { - "type": "object", - "properties": { - "hide_temperature": { - "title": "Hide Indoor/Outdoor's Temperature Sensor", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' && model.options.devices[arrayIndices].deviceId);" - } + { + "title": "Faucet", + "enum": [ + "faucet" + ] }, - "convertUnitTo": { - "title": "Convert Indoor/Outdoor Sensor's Temperature Unit To", - "type": "string", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].iosensor.hide_temperature);" - }, - "oneOf": [ - { - "title": "Celsius", - "enum": [ - "CELSIUS" - ] - }, - { - "title": "Fahrenheit", - "enum": [ - "FAHRENHEIT" - ] - } + { + "title": "Garage Door", + "enum": [ + "garagedoor" ] }, - "hide_humidity": { - "title": "Hide Indoor/Outdoor's Humidity Sensor", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' && model.options.devices[arrayIndices].deviceId);" - } - } - } - }, - "waterdetector": { - "type": "object", - "properties": { - "hide_leak": { - "title": "Hide Water Detector's Leak Sensor", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Water Detector' && model.options.devices[arrayIndices].deviceId);" - } - }, - "dry": { - "title": "Water Detector's Dry Alert", - "type": "boolean", - "description": "If true, the Water Detector will send an alert when it is dry.", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Water Detector' && model.options.devices[arrayIndices].deviceId);" - } - } - } - }, - "humidifier": { - "type": "object", - "properties": { - "hide_temperature": { - "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].configDeviceType === 'Humidifier' && model.options.devices[arrayIndices].deviceId);" - } - }, - "set_minStep": { - "title": "Set Min Step", - "type": "number", - "placeholder": "1", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Humidifier' && model.options.devices[arrayIndices].deviceId);" - } - } - } - }, - "curtain": { - "type": "object", - "properties": { - "set_minStep": { - "title": "Set Minimum Step", - "type": "number", - "placeholder": "1", - "description": "Sets the minimum steps that curtain allows. So if set to 20, it would allow you to set the curtain state to 0, 20, 40, 60, 80, and 100", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3') && model.options.devices[arrayIndices].deviceId);" - } - }, - "set_min": { - "title": "Set Minimum Open State", - "type": "number", - "placeholder": "0", - "description": "Sets the minimum percentage before needed to set Curtain to Open", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3') && model.options.devices[arrayIndices].deviceId);" - } - }, - "set_max": { - "title": "Set Maximum Close State", - "type": "number", - "placeholder": "100", - "description": "Sets the maximum percentage before needing to set Curtain to Closed", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3') && model.options.devices[arrayIndices].deviceId);" - } - }, - "updateRate": { - "title": "Curtain Update Rate", - "type": "number", - "minimum": 1, - "placeholder": 5, - "description": "Indicates the number of seconds 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].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3') && model.options.devices[arrayIndices].deviceId);" - } - }, - "disable_group": { - "title": "Disable Grouping", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3') && model.options.devices[arrayIndices].deviceId);" - } - }, - "setOpenMode": { - "title": "Curtain's Opening Mode", - "type": "string", - "oneOf": [ - { - "enum": [ - "0" - ], - "title": "Performance Mode" - }, - { - "enum": [ - "1" - ], - "title": "Silent Mode" - } - ], - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3') && model.options.devices[arrayIndices].deviceId);" - } - }, - "setCloseMode": { - "title": "Curtain's Closing Mode", - "type": "string", - "oneOf": [ - { - "enum": [ - "0" - ], - "title": "Performance Mode" - }, - { - "enum": [ - "1" - ], - "title": "Silent Mode" - } - ], - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3') && model.options.devices[arrayIndices].deviceId);" - } - }, - "silentModeSwitch": { - "title": "Enable Curtain Silent Mode Switches", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3') && model.options.devices[arrayIndices].deviceId);" - } - }, - "hide_lightsensor": { - "title": "Hide Light Sensor", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3') && 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].configDeviceType === 'Curtain3') && 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].configDeviceType === 'Curtain3') && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].curtain.hide_lightsensor);" - } - } - } - }, - "blindTilt": { - "type": "object", - "properties": { - "mode": { - "title": "Mapping Mode", - "type": "string", - "oneOf": [ - { - "enum": [ - "down_and_up" - ], - "title": "Down-to-Up" - }, - { - "enum": [ - "only_down" - ], - "title": "Middle to Down" - }, - { - "enum": [ - "only_up" - ], - "title": "Middle to Up" - }, - { - "enum": [ - "up_and_down" - ], - "title": "Up-to-Down" - }, - { - "enum": [ - "use_tilt_for_direction" - ], - "title": "Tilt Direction" - } - ], - "description": "Defines how the Home app sliders control the blind tilt.", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' && model.options.devices[arrayIndices].deviceId);" - } - }, - "set_minStep": { - "title": "Set Minimum Step", - "type": "number", - "placeholder": "1", - "description": "Sets the minimum steps that blind tilt allows. So if set to 20, it would allow you to set the blind tilt state to 0, 20, 40, 60, 80, and 100", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' && model.options.devices[arrayIndices].deviceId);" - } - }, - "set_min": { - "title": "Set Minimum Open State", - "type": "number", - "placeholder": "0", - "description": "Sets the minimum percentage before needed to set Blind Tilt to Open", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' && model.options.devices[arrayIndices].deviceId);" - } - }, - "set_max": { - "title": "Set Maximum Close State", - "type": "number", - "placeholder": "100", - "description": "Sets the maximum percentage before needing to set Blind Tilt to Closed", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' && model.options.devices[arrayIndices].deviceId);" - } - }, - "hide_lightsensor": { - "title": "Hide Light Sensor", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' && model.options.devices[arrayIndices].deviceId);" - } - }, - "updateRate": { - "title": "Blind Tilt Update Rate", - "type": "number", - "minimum": 1, - "placeholder": 5, - "description": "Indicates the number of seconds before refreshing Blind Tilt status while updating slide progress.", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' && model.options.devices[arrayIndices].deviceId);" - } - }, - "setOpenMode": { - "title": "Blind Tilt's Opening Mode", - "type": "string", - "oneOf": [ - { - "enum": [ - "0" - ], - "title": "Performance Mode" - }, - { - "enum": [ - "1" - ], - "title": "Silent Mode" - } - ], - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' && model.options.devices[arrayIndices].deviceId);" - } - }, - "setCloseMode": { - "title": "Blind Tilt's Closing Mode", - "type": "string", - "oneOf": [ - { - "enum": [ - "0" - ], - "title": "Performance Mode" - }, - { - "enum": [ - "1" - ], - "title": "Silent Mode" - } - ], - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' && model.options.devices[arrayIndices].deviceId);" - } + { + "title": "Lock", + "enum": [ + "lock" + ] }, - "silentModeSwitch": { - "title": "Enable Blind Tilt Silent Mode Switches", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' && model.options.devices[arrayIndices].deviceId);" - } + { + "title": "Outlet", + "enum": [ + "outlet" + ] + }, + { + "title": "Stateful Programmable Switch (Only Works in 3rd Party Home Apps)", + "enum": [ + "stateful" + ] + }, + { + "title": "Switch", + "enum": [ + "switch" + ] + }, + { + "title": "Window", + "enum": [ + "window" + ] + }, + { + "title": "Window Covering", + "enum": [ + "windowcovering" + ] } - } + ] }, - "contact": { - "type": "object", - "properties": { - "hide_lightsensor": { - "title": "Hide Contact Sensor's Light Sensor", - "type": "boolean", - "condition": { - "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);" - } + "mode": { + "title": "Bot Mode", + "type": "string", + "required": true, + "default": "", + "oneOf": [ + { + "title": "Default", + "enum": [""] }, - "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 === 'Contact Sensor' && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].contact.hide_lightsensor);" - } + { + "title": "Multi-Press", + "enum": ["multipress"] }, - "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 === 'Contact Sensor' && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].contact.hide_lightsensor);" - } + { + "title": "Press", + "enum": ["press"] }, - "hide_motionsensor": { - "title": "Hide Contact Sensor's Motion Sensor", - "type": "boolean", - "condition": { - "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);" - } + { + "title": "Switch", + "enum": ["switch"] } + ], + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Bot' && model.options.devices[arrayIndices].deviceId);" } }, - "motion": { - "type": "object", - "properties": { - "hide_lightsensor": { - "title": "Hide Motion Sensor's Light Sensor", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Motion Sensor' && model.options.devices[arrayIndices].deviceId);" - } + "mapping": { + "title": "Mapping Mode", + "type": "string", + "required": true, + "default": "", + "oneOf": [ + { + "title": "Default", + "enum": [""] }, - "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 === 'Motion Sensor' && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].motion.hide_lightsensor);" - } + { + "title": "Down and Up", + "enum": ["down_and_up"] }, - "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 === 'Motion Sensor' && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].motion.hide_lightsensor);" - } + { + "title": "Only Down", + "enum": ["only_down"] + }, + { + "title": "Only Up", + "enum": ["only_up"] + }, + { + "title": "Up and Down", + "enum": ["up_and_down"] + }, + { + "title": "Use Tilt for Direction", + "enum": ["use_tilt_for_direction"] } + ], + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' && model.options.devices[arrayIndices].deviceId);" } }, - "colorbulb": { - "type": "object", - "properties": { - "set_minStep": { - "title": "Set Min Step", - "type": "number", - "placeholder": "1", - "condition": { - "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);" - } - }, - "adaptiveLightingShift": { - "title": "Adaptive Lighting Shift", - "type": "number", - "placeholder": "-1", - "description": "The mired for each Adaptive Lighting update will be increased by this value, making the light appear warmer. Set to -1 to remove Adaptive Lighting feature. Must be -1 or more.", - "condition": { - "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);" - } - } + "allowPush": { + "title": "Allow Changes to Be Pushed", + "description": "If set to true, this will allow commands to be sent even if device state is already in state that is being pushed.", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId);" } }, - "striplight": { - "type": "object", - "properties": { - "set_minStep": { - "title": "Set Min Step", - "type": "number", - "placeholder": "1", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Strip Light' && model.options.devices[arrayIndices].deviceId);" - } - }, - "adaptiveLightingShift": { - "title": "Adaptive Lighting Shift", - "type": "number", - "placeholder": "-1", - "description": "The mired for each Adaptive Lighting update will be increased by this value, making the light appear warmer. Set to -1 to remove Adaptive Lighting feature. Must be -1 or more.", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Strip Light' && model.options.devices[arrayIndices].deviceId);" - } - } + "doublePress": { + "title": "How many presses do you want to simulate?", + "type": "number", + "placeholder": "2", + "condition": { + "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].mode);" } }, - "lock": { - "type": "object", - "properties": { - "hide_contactsensor": { - "title": "Hide Lock's Contact Sensor", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Smart Lock' || model.options.devices[arrayIndices].configDeviceType === 'Smart Lock Pro') && model.options.devices[arrayIndices].deviceId);" - } + "pushRatePress": { + "title": "Double Press Push Rate", + "type": "number", + "placeholder": "15", + "description": "Indicates the number of seconds between pushes for double presses to SwitchBot API", + "condition": { + "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].mode && model.options.devices[arrayIndices].doublePress > 1);" + } + }, + "hide_temperature": { + "title": "Hide Temperature Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Humidifier' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)') && model.options.devices[arrayIndices].deviceId);" + } + }, + "convertUnitTo": { + "title": "Convert Temperature Unit To", + "type": "string", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Humidifier' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)') && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_temperature);" + }, + "oneOf": [ + { + "title": "Celsius", + "enum": [ + "CELSIUS" + ] }, - "activate_latchbutton": { - "title": "Activate Latch Button", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Smart Lock' || model.options.devices[arrayIndices].configDeviceType === 'Smart Lock Pro') && model.options.devices[arrayIndices].deviceId);" - } + { + "title": "Fahrenheit", + "enum": [ + "FAHRENHEIT" + ] } + ] + }, + "hide_humidity": { + "title": "Hide Humidity Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)') && model.options.devices[arrayIndices].deviceId);" } }, - "disableCaching": { - "title": "Disable Caching", + "hide_lightsensor": { + "title": "Hide Light Sensor", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3'));" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Contact Sensor' || model.options.devices[arrayIndices].configDeviceType === 'Motion Sensor' || model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' || model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3' || model.options.devices[arrayIndices].configDeviceType === 'WoRollerShade' || model.options.devices[arrayIndices].configDeviceType === 'Roller Shade') && model.options.devices[arrayIndices].deviceId);" } }, - "maxRetries": { - "title": "Device Max Retries for OpenAPI", - "type": "number", - "placeholder": 5, + "hide_motionsensor": { + "title": "Hide Contact Sensor's Motion Sensor", + "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].connectionType === 'OpenAPI' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI'));" + "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);" } }, - "delayBetweenRetries": { - "title": "Device Delay Between Retries for OpenAPI (In Seconds)", - "type": "number", - "placeholder": 3, + "hide_contactsensor": { + "title": "Hide Lock's Contact Sensor", + "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].connectionType === 'OpenAPI' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI'));" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Smart Lock' || model.options.devices[arrayIndices].configDeviceType === 'Smart Lock Pro') && model.options.devices[arrayIndices].deviceId);" } }, - "maxRetry": { - "title": "Max Retries for BLE", - "type": "number", - "placeholder": "5", + "hide_leak": { + "title": "Hide Water Detector's Leak Sensor", + "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].connectionType === 'BLE' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI') && (model.options.devices[arrayIndices].configDeviceType === 'Bot' || model.options.devices[arrayIndices].configDeviceType === 'Robot Vacuum Cleaner S1' || model.options.devices[arrayIndices].configDeviceType === 'Robot Vacuum Cleaner S1 Plus' || model.options.devices[arrayIndices].configDeviceType === 'Strip Light' || model.options.devices[arrayIndices].configDeviceType === 'Plug' || model.options.devices[arrayIndices].configDeviceType === 'Plug Mini (JP)' || model.options.devices[arrayIndices].configDeviceType === 'Plug Mini (US)' || model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3' || model.options.devices[arrayIndices].configDeviceType === 'Color Bulb' || model.options.devices[arrayIndices].configDeviceType === 'Ceiling Light' || model.options.devices[arrayIndices].configDeviceType === 'Ceiling Light Pro' || model.options.devices[arrayIndices].configDeviceType === 'Motion Sensor'));" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Water Detector' && model.options.devices[arrayIndices].deviceId);" } }, - "mqttURL": { - "title": "MQTT URL (Device Specific)", - "type": "string", - "placeholder": "192.168.7.1", + "disable_group": { + "title": "Disable Grouping", + "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Water Detector' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3' || model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' || model.options.devices[arrayIndices].configDeviceType === 'WoRollerShade' || model.options.devices[arrayIndices].configDeviceType === 'Roller Shade') && model.options.devices[arrayIndices].deviceId);" } }, - "mqttOptions": { - "title": "MQTT Options (Device Specific)", - "type": "string", + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].mqttURL && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Water Detector' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId);" } }, - "mqttPubOptions": { - "title": "MQTT Pub Options (Device Specific)", - "type": "string", + "adaptiveLightingShift": { + "title": "Adaptive Lighting Shift", + "type": "number", + "placeholder": "-1", + "description": "The mired for each Adaptive Lighting update will be increased by this value, making the light appear warmer. Set to -1 to remove Adaptive Lighting feature. Must be -1 or more.", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].mqttURL && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Water Detector' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Color Bulb' || model.options.devices[arrayIndices].configDeviceType === 'Strip Light' || model.options.devices[arrayIndices].configDeviceType === 'Ceiling Light' || model.options.devices[arrayIndices].configDeviceType === 'Ceiling Light Pro') && model.options.devices[arrayIndices].deviceId);" } }, - "history": { - "title": "EVE History", + "activate_latchbutton": { + "title": "Activate Latch Button", "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Water Detector' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Smart Lock' || model.options.devices[arrayIndices].configDeviceType === 'Smart Lock Pro') && model.options.devices[arrayIndices].deviceId);" } }, - "firmware": { - "title": "Firmware Override", - "type": "string", - "placeholder": "1.2.8", + "dry": { + "title": "Water Detector's Dry Alert", + "type": "boolean", + "description": "If true, the Water Detector will send an alert when it is dry.", "condition": { - "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].configDeviceType);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType === 'Water Detector' && model.options.devices[arrayIndices].deviceId);" } }, - "refreshRate": { - "title": "Device Refresh Rate", + "set_minStep": { + "title": "Set Minimum Step", "type": "number", - "placeholder": 360, + "placeholder": "1", + "description": "Sets the minimum steps device will take. So if set to 20, it would allow you to set the curtain state to 0, 20, 40, 60, 80, and 100", "condition": { - "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Strip Light' || model.options.devices[arrayIndices].configDeviceType === 'Color Bulb' || model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' || model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3' || model.options.devices[arrayIndices].configDeviceType === 'WoRollerShade' || model.options.devices[arrayIndices].configDeviceType === 'Roller Shade' || model.options.devices[arrayIndices].configDeviceType === 'Humidifier') && model.options.devices[arrayIndices].deviceId);" } }, - "updateRate": { - "title": "Device Update Rate", + "set_min": { + "title": "Set Minimum Open State", "type": "number", - "placeholder": 360, + "placeholder": "0", + "description": "Sets the minimum percentage before needed to set to Open", "condition": { - "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' || model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3' || model.options.devices[arrayIndices].configDeviceType === 'WoRollerShade' || model.options.devices[arrayIndices].configDeviceType === 'Roller Shade' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Motion Sensor' || model.options.devices[arrayIndices].configDeviceType === 'Contact Sensor' || model.options.devices[arrayIndices].configDeviceType === 'Humidifier' || model.options.devices[arrayIndices].configDeviceType === 'Strip Light' || model.options.devices[arrayIndices].configDeviceType === 'Ceiling Light' || model.options.devices[arrayIndices].configDeviceType === 'Ceiling Light Pro') && model.options.devices[arrayIndices].deviceId);" } }, - "pushRate": { - "title": "Device Push Rate", + "set_max": { + "title": "Set Maximum Close State", "type": "number", - "placeholder": 360, + "placeholder": "100", + "description": "Sets the maximum percentage before needing to set to Closed", "condition": { - "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' || model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3' || model.options.devices[arrayIndices].configDeviceType === 'WoRollerShade' || model.options.devices[arrayIndices].configDeviceType === 'Roller Shade' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Motion Sensor' || model.options.devices[arrayIndices].configDeviceType === 'Contact Sensor' || model.options.devices[arrayIndices].configDeviceType === 'Humidifier' || model.options.devices[arrayIndices].configDeviceType === 'Strip Light' || model.options.devices[arrayIndices].configDeviceType === 'Ceiling Light' || model.options.devices[arrayIndices].configDeviceType === 'Ceiling Light Pro') && model.options.devices[arrayIndices].deviceId);" } }, - "offline": { - "title": "Offline as Off", - "type": "boolean", + "set_minlux": { + "title": "Set Min Lux", + "type": "number", + "placeholder": "1", "condition": { - "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Contact Sensor' || model.options.devices[arrayIndices].configDeviceType === 'Motion Sensor' || model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' || model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3' || model.options.devices[arrayIndices].configDeviceType === 'WoRollerShade' || model.options.devices[arrayIndices].configDeviceType === 'Roller Shade') && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_lightsensor);" } }, - "external": { - "title": "External Accessory", - "type": "boolean", + "set_maxlux": { + "title": "Set Max Lux", + "type": "number", + "placeholder": "6001", "condition": { - "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Contact Sensor' || model.options.devices[arrayIndices].configDeviceType === 'Motion Sensor' || model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' || model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3' || model.options.devices[arrayIndices].configDeviceType === 'WoRollerShade' || model.options.devices[arrayIndices].configDeviceType === 'Roller Shade') && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_lightsensor);" } }, - "logging": { - "title": "Device Logging Override Setting", + "setOpenMode": { + "title": "Opening Mode", "type": "string", - "required": true, - "default": "", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3' || model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' || model.options.devices[arrayIndices].configDeviceType === 'WoRollerShade' || model.options.devices[arrayIndices].configDeviceType === 'Roller Shade') && model.options.devices[arrayIndices].deviceId);" + }, "oneOf": [ { - "title": "Debug Logging", "enum": [ - "debug" - ] + "0" + ], + "title": "Performance Mode" }, { - "title": "Default Logging", "enum": [ - "" - ] - }, + "1" + ], + "title": "Silent Mode" + } + ] + }, + "setCloseMode": { + "title": "Closing Mode", + "type": "string", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3' || model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' || model.options.devices[arrayIndices].configDeviceType === 'WoRollerShade' || model.options.devices[arrayIndices].configDeviceType === 'Roller Shade') && model.options.devices[arrayIndices].deviceId);" + }, + "oneOf": [ { - "title": "No Logging", "enum": [ - "none" - ] + "0" + ], + "title": "Performance Mode" }, { - "title": "Standard Logging", "enum": [ - "standard" - ] + "1" + ], + "title": "Silent Mode" } - ], + ] + }, + "silentModeSwitch": { + "title": "Enable Silent Mode Switches", + "type": "boolean", "condition": { - "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3' || model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' || model.options.devices[arrayIndices].configDeviceType === 'WoRollerShade' || model.options.devices[arrayIndices].configDeviceType === 'Roller Shade') && model.options.devices[arrayIndices].deviceId);" } - } - }, - "required": [ - "deviceId", - "configDeviceType", - "configDeviceName", - "connectionType", - "logging" - ] - }, - "uniqueItems": true - }, - "irdevices": { - "type": "array", - "items": { - "title": "IR Devices", - "type": "object", - "properties": { - "deviceId": { - "title": "Device ID", + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].connectionType === 'BLE' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].connectionType === 'OpenAPI' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].connectionType === 'OpenAPI' || model.options.devices[arrayIndices].connectionType === 'BLE/OpenAPI'));" + } + }, + "mqttURL": { + "title": "MQTT URL (Device Specific)", "type": "string", - "placeholder": "81F3UT59513F" + "placeholder": "192.168.7.1", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Water Detector' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" + } }, - "configDeviceName": { - "title": "Device Name", + "mqttOptions": { + "title": "MQTT Options (Device Specific)", "type": "string", - "placeholder": "SwitchBot", "condition": { - "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].mqttURL && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Water Detector' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" } }, - "hide_device": { - "title": "Hide Device", + "mqttPubOptions": { + "title": "MQTT Pub Options (Device Specific)", + "type": "string", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].mqttURL && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Water Detector' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" + } + }, + "history": { + "title": "EVE History", "type": "boolean", - "description": "If true, device will be removed or hidden from HomeKit.", "condition": { - "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId);" + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Water Detector' || model.options.devices[arrayIndices].configDeviceType === 'WoIOSensor' || model.options.devices[arrayIndices].configDeviceType === 'Hub 2' || model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)'));" } }, - "configRemoteType": { - "title": "Remote Type", + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].configDeviceType);" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" + } + }, + "logging": { + "title": "Device Logging Override Setting", "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" + }, "oneOf": [ { - "title": "Air Conditioner (IR)", - "enum": [ - "Air Conditioner" - ] - }, - { - "title": "Air Purifier (IR)", + "title": "Debug Logging", "enum": [ - "Air Purifier" + "debug" ] }, { - "title": "Camera (IR)", + "title": "Default Logging", "enum": [ - "Camera" + "" ] }, { - "title": "DIY Air Conditioner (IR)", + "title": "No Logging", "enum": [ - "DIY Air Conditioner" + "none" ] }, { - "title": "DIY Air Purifier (IR)", + "title": "Standard Logging", "enum": [ - "DIY Air Purifier" + "standard" ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Curtain' || model.options.devices[arrayIndices].configDeviceType === 'Curtain3' || model.options.devices[arrayIndices].configDeviceType === 'Blind Tilt' || model.options.devices[arrayIndices].configDeviceType === 'WoRollerShade' || model.options.devices[arrayIndices].configDeviceType === 'Roller Shade') && model.options.devices[arrayIndices].deviceId);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);" + } + } + }, + "required": [ + "deviceId", + "configDeviceType", + "configDeviceName", + "connectionType", + "logging" + ] + }, + "uniqueItems": true + }, + "deviceConfig": { + "type": "object", + "properties": { + "Humidifier": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Humidifiers", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "hide_temperature": { + "title": "Hide Temperature Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Humidifier'].hide_device);" + } + }, + "convertUnitTo": { + "title": "Convert Temperature Unit To", + "type": "string", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Humidifier'].hide_device);" }, - { - "title": "DIY Camera (IR)", - "enum": [ - "DIY Camera" - ] + "oneOf": [ + { + "title": "Celsius", + "enum": [ + "CELSIUS" + ] + }, + { + "title": "Fahrenheit", + "enum": [ + "FAHRENHEIT" + ] + } + ] + }, + "set_minStep": { + "title": "Set Minimum Step", + "type": "number", + "placeholder": "1", + "description": "Sets the minimum steps device will take. So if set to 20, it would allow you to set the curtain state to 0, 20, 40, 60, 80, and 100", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Humidifier'].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Humidifier'] && !model.options.deviceConfig['Humidifier'].hide_device);" }, - { - "title": "DIY DVD (IR)", - "enum": [ - "DIY DVD" - ] + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Humidifier'] && !model.options.deviceConfig['Humidifier'].hide_device && (model.options.deviceConfig['Humidifier'].connectionType === 'BLE' || model.options.deviceConfig['Humidifier'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Humidifiers", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Humidifier'] && !model.options.deviceConfig['Humidifier'].hide_device && (model.options.deviceConfig['Humidifier'].connectionType === 'OpenAPI' || model.options.deviceConfig['Humidifier'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Humidifier'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Humidifier'] && !model.options.deviceConfig['Humidifier'].hide_device && (model.options.deviceConfig['Humidifier'].connectionType === 'BLE' || model.options.deviceConfig['Humidifier'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Humidifier'] && !model.options.deviceConfig['Humidifier'].hide_device && (model.options.deviceConfig['Humidifier'].connectionType === 'OpenAPI' || model.options.deviceConfig['Humidifier'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Humidifier'] && !model.options.deviceConfig['Humidifier'].hide_device && (model.options.deviceConfig['Humidifier'].connectionType === 'OpenAPI' || model.options.deviceConfig['Humidifier'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Humidifier'] && !model.options.deviceConfig['Humidifier'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Humidifier'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Humidifier'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Humidifier'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Humidifier'].hide_device);" }, - { - "title": "DIY Fan (IR)", - "enum": [ - "DIY Fan" - ] + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Humidifier'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Humidifier'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Humidifier'].hide_device);" + } + } + } + }, + "Hub 2": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Hub 2s", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "hide_temperature": { + "title": "Hide Temperature Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Hub 2'] && !model.options.deviceConfig['Hub 2'].hide_device);" + } + }, + "convertUnitTo": { + "title": "Convert Temperature Unit To", + "type": "string", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Hub 2'] && !model.options.deviceConfig['Hub 2'].hide_device);" }, - { - "title": "DIY IPTV (IR)", - "enum": [ - "DIY IPTV" - ] + "oneOf": [ + { + "title": "Celsius", + "enum": [ + "CELSIUS" + ] + }, + { + "title": "Fahrenheit", + "enum": [ + "FAHRENHEIT" + ] + } + ] + }, + "hide_humidity": { + "title": "Hide Humidity Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Hub 2'] && !model.options.deviceConfig['Hub 2'].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Hub 2'] && !model.options.deviceConfig['Hub 2'].hide_device);" }, - { - "title": "DIY Light (IR)", - "enum": [ - "DIY Light" - ] + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Hub 2'] && !model.options.deviceConfig['Hub 2'].hide_device && (model.options.deviceConfig['Hub 2'].connectionType === 'BLE' || model.options.deviceConfig['Hub 2'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Hub 2s", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Hub 2'] && !model.options.deviceConfig['Hub 2'].hide_device && (model.options.deviceConfig['Hub 2'].connectionType === 'OpenAPI' || model.options.deviceConfig['Hub 2'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Hub 2'] && !model.options.deviceConfig['Hub 2'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Hub 2'] && !model.options.deviceConfig['Hub 2'].hide_device && (model.options.deviceConfig['Hub 2'].connectionType === 'BLE' || model.options.deviceConfig['Hub 2'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Hub 2'] && !model.options.deviceConfig['Hub 2'].hide_device && (model.options.deviceConfig['Hub 2'].connectionType === 'OpenAPI' || model.options.deviceConfig['Hub 2'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Hub 2'] && !model.options.deviceConfig['Hub 2'].hide_device && (model.options.deviceConfig['Hub 2'].connectionType === 'OpenAPI' || model.options.deviceConfig['Hub 2'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Hub 2'] && !model.options.deviceConfig['Hub 2'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Hub 2'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Hub 2'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Hub 2'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Hub 2'].hide_device);" }, - { - "title": "DIY Projector (IR)", - "enum": [ - "DIY Projector" - ] + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Hub 2'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Hub 2'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Hub 2'].hide_device);" + } + } + } + }, + "Bot": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Bots", + "type": "boolean", + "description": "If true, all Bot devices will be removed or hidden from HomeKit." + }, + "mode": { + "title": "Bot Mode", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Bot'] && !model.options.deviceConfig['Bot'].hide_device);" + }, + "oneOf": [ + { + "title": "Default", + "enum": [""] + }, + { + "title": "Multi-Press", + "enum": ["multipress"] + }, + { + "title": "Press", + "enum": ["press"] + }, + { + "title": "Switch", + "enum": ["switch"] + } + ] + }, + "type": { + "title": "What Type of Device do you want to display in the Home App?", + "type": "string", + "required": true, + "default": "", + "placeholder": "outlet", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Bot'] && !model.options.deviceConfig['Bot'].hide_device);" + }, + "oneOf": [ + { + "title": "Default", + "enum": [ + "" + ] + }, + { + "title": "Door", + "enum": [ + "door" + ] + }, + { + "title": "Fan", + "enum": [ + "fan" + ] + }, + { + "title": "Faucet", + "enum": [ + "faucet" + ] + }, + { + "title": "Garage Door", + "enum": [ + "garagedoor" + ] + }, + { + "title": "Lock", + "enum": [ + "lock" + ] + }, + { + "title": "Outlet", + "enum": [ + "outlet" + ] + }, + { + "title": "Stateful Programmable Switch (Only Works in 3rd Party Home Apps)", + "enum": [ + "stateful" + ] + }, + { + "title": "Switch", + "enum": [ + "switch" + ] + }, + { + "title": "Window", + "enum": [ + "window" + ] + }, + { + "title": "Window Covering", + "enum": [ + "windowcovering" + ] + } + ] + }, + "doublePress": { + "title": "How many presses do you want to simulate?", + "type": "number", + "placeholder": "2", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Bot'] && !model.options.deviceConfig['Bot'].hide_device);" + } + }, + "pushRatePress": { + "title": "Double Press Push Rate", + "type": "number", + "placeholder": "15", + "description": "Indicates the number of seconds between pushes for double presses to SwitchBot API", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Bot'] && !model.options.deviceConfig['Bot'].hide_device);" + } + }, + "allowPush": { + "title": "Allow Changes to Be Pushed", + "description": "If set to true, this will allow commands to be sent even if device state is already in state that is being pushed.", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Bot'] && !model.options.deviceConfig['Bot'].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Bot'] && !model.options.deviceConfig['Bot'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Bot'] && !model.options.deviceConfig['Bot'].hide_device && (model.options.deviceConfig['Bot'].connectionType === 'BLE' || model.options.deviceConfig['Bot'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Hub 2s", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Bot'] && !model.options.deviceConfig['Bot'].hide_device && (model.options.deviceConfig['Bot'].connectionType === 'OpenAPI' || model.options.deviceConfig['Bot'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Bot'] && !model.options.deviceConfig['Bot'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Bot'] && !model.options.deviceConfig['Bot'].hide_device && (model.options.deviceConfig['Bot'].connectionType === 'BLE' || model.options.deviceConfig['Bot'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Bot'] && !model.options.deviceConfig['Bot'].hide_device && (model.options.deviceConfig['Bot'].connectionType === 'OpenAPI' || model.options.deviceConfig['Bot'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Bot'] && !model.options.deviceConfig['Bot'].hide_device && (model.options.deviceConfig['Bot'].connectionType === 'OpenAPI' || model.options.deviceConfig['Bot'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Bot'] && !model.options.deviceConfig['Bot'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Bot'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Bot'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Bot'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Bot'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Bot'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Bot'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Bot'].hide_device);" + } + } + } + }, + "Meter": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Meters", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "hide_temperature": { + "title": "Hide Temperature Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter'] && !model.options.deviceConfig['Meter'].hide_device);" + } + }, + "convertUnitTo": { + "title": "Convert Temperature Unit To", + "type": "string", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter'] && !model.options.deviceConfig['Meter'].hide_device);" + }, + "oneOf": [ + { + "title": "Celsius", + "enum": [ + "CELSIUS" + ] + }, + { + "title": "Fahrenheit", + "enum": [ + "FAHRENHEIT" + ] + } + ] + }, + "hide_humidity": { + "title": "Hide Humidity Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter'] && !model.options.deviceConfig['Meter'].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter'] && !model.options.deviceConfig['Meter'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter'] && !model.options.deviceConfig['Meter'].hide_device && (model.options.deviceConfig['Meter'].connectionType === 'BLE' || model.options.deviceConfig['Meter'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Meters", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter'] && !model.options.deviceConfig['Meter'].hide_device && (model.options.deviceConfig['Meter'].connectionType === 'OpenAPI' || model.options.deviceConfig['Meter'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter'] && !model.options.deviceConfig['Meter'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter'] && !model.options.deviceConfig['Meter'].hide_device && (model.options.deviceConfig['Meter'].connectionType === 'BLE' || model.options.deviceConfig['Meter'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter'] && !model.options.deviceConfig['Meter'].hide_device && (model.options.deviceConfig['Meter'].connectionType === 'OpenAPI' || model.options.deviceConfig['Meter'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter'] && !model.options.deviceConfig['Meter'].hide_device && (model.options.deviceConfig['Meter'].connectionType === 'OpenAPI' || model.options.deviceConfig['Meter'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter'] && !model.options.deviceConfig['Meter'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Meter'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Meter'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Meter'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Meter'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Meter'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Meter'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Meter'].hide_device);" + } + } + } + }, + "MeterPlus": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Meter Pluses", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "hide_temperature": { + "title": "Hide Temperature Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['MeterPlus'] && !model.options.deviceConfig['MeterPlus'].hide_device);" + } + }, + "convertUnitTo": { + "title": "Convert Temperature Unit To", + "type": "string", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['MeterPlus'] && !model.options.deviceConfig['MeterPlus'].hide_device);" + }, + "oneOf": [ + { + "title": "Celsius", + "enum": [ + "CELSIUS" + ] + }, + { + "title": "Fahrenheit", + "enum": [ + "FAHRENHEIT" + ] + } + ] + }, + "hide_humidity": { + "title": "Hide Humidity Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['MeterPlus'] && !model.options.deviceConfig['MeterPlus'].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['MeterPlus'] && !model.options.deviceConfig['MeterPlus'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['MeterPlus'] && !model.options.deviceConfig['MeterPlus'].hide_device && (model.options.deviceConfig['MeterPlus'].connectionType === 'BLE' || model.options.deviceConfig['MeterPlus'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Meter Pluses", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['MeterPlus'] && !model.options.deviceConfig['MeterPlus'].hide_device && (model.options.deviceConfig['MeterPlus'].connectionType === 'OpenAPI' || model.options.deviceConfig['MeterPlus'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['MeterPlus'] && !model.options.deviceConfig['MeterPlus'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['MeterPlus'] && !model.options.deviceConfig['MeterPlus'].hide_device && (model.options.deviceConfig['MeterPlus'].connectionType === 'BLE' || model.options.deviceConfig['MeterPlus'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['MeterPlus'] && !model.options.deviceConfig['MeterPlus'].hide_device && (model.options.deviceConfig['MeterPlus'].connectionType === 'OpenAPI' || model.options.deviceConfig['MeterPlus'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['MeterPlus'] && !model.options.deviceConfig['MeterPlus'].hide_device && (model.options.deviceConfig['MeterPlus'].connectionType === 'OpenAPI' || model.options.deviceConfig['MeterPlus'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['MeterPlus'] && !model.options.deviceConfig['MeterPlus'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['MeterPlus'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['MeterPlus'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['MeterPlus'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['MeterPlus'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['MeterPlus'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['MeterPlus'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['MeterPlus'].hide_device);" + } + } + } + }, + "Meter Plus (JP)": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Hub Meter Plus (JP)s", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "hide_temperature": { + "title": "Hide Temperature Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter Plus (JP)'] && !model.options.deviceConfig['Meter Plus (JP)'].hide_device);" + } + }, + "convertUnitTo": { + "title": "Convert Temperature Unit To", + "type": "string", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter Plus (JP)'] && !model.options.deviceConfig['Meter Plus (JP)'].hide_device);" + }, + "oneOf": [ + { + "title": "Celsius", + "enum": [ + "CELSIUS" + ] + }, + { + "title": "Fahrenheit", + "enum": [ + "FAHRENHEIT" + ] + } + ] + }, + "hide_humidity": { + "title": "Hide Humidity Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter Plus (JP)'] && !model.options.deviceConfig['Meter Plus (JP)'].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter Plus (JP)'] && !model.options.deviceConfig['Meter Plus (JP)'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter Plus (JP)'] && !model.options.deviceConfig['Meter Plus (JP)'].hide_device && (model.options.deviceConfig['Meter Plus (JP)'].connectionType === 'BLE' || model.options.deviceConfig['Meter Plus (JP)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Meter Plus (JP)s", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter Plus (JP)'] && !model.options.deviceConfig['Meter Plus (JP)'].hide_device && (model.options.deviceConfig['Meter Plus (JP)'].connectionType === 'OpenAPI' || model.options.deviceConfig['Meter Plus (JP)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter Plus (JP)'] && !model.options.deviceConfig['Meter Plus (JP)'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter Plus (JP)'] && !model.options.deviceConfig['Meter Plus (JP)'].hide_device && (model.options.deviceConfig['Meter Plus (JP)'].connectionType === 'BLE' || model.options.deviceConfig['Meter Plus (JP)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter Plus (JP)'] && !model.options.deviceConfig['Meter Plus (JP)'].hide_device && (model.options.deviceConfig['Meter Plus (JP)'].connectionType === 'OpenAPI' || model.options.deviceConfig['Meter Plus (JP)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter Plus (JP)'] && !model.options.deviceConfig['Meter Plus (JP)'].hide_device && (model.options.deviceConfig['Meter Plus (JP)'].connectionType === 'OpenAPI' || model.options.deviceConfig['Meter Plus (JP)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Meter Plus (JP)'] && !model.options.deviceConfig['Meter Plus (JP)'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Meter Plus (JP)'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Meter Plus (JP)'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Meter Plus (JP)'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Meter Plus (JP)'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Meter Plus (JP)'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Meter Plus (JP)'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Meter Plus (JP)'].hide_device);" + } + } + } + }, + "WoIOSensor": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All WoIOSensors", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "hide_temperature": { + "title": "Hide Temperature Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoIOSensor'] && !model.options.deviceConfig['WoIOSensor'].hide_device);" + } + }, + "convertUnitTo": { + "title": "Convert Temperature Unit To", + "type": "string", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoIOSensor'] && !model.options.deviceConfig['WoIOSensor'].hide_device);" + }, + "oneOf": [ + { + "title": "Celsius", + "enum": [ + "CELSIUS" + ] + }, + { + "title": "Fahrenheit", + "enum": [ + "FAHRENHEIT" + ] + } + ] + }, + "hide_humidity": { + "title": "Hide Humidity Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoIOSensor'] && !model.options.deviceConfig['WoIOSensor'].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoIOSensor'] && !model.options.deviceConfig['WoIOSensor'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoIOSensor'] && !model.options.deviceConfig['WoIOSensor'].hide_device && (model.options.deviceConfig['WoIOSensor'].connectionType === 'BLE' || model.options.deviceConfig['WoIOSensor'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all WoIOSensors", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoIOSensor'] && !model.options.deviceConfig['WoIOSensor'].hide_device && (model.options.deviceConfig['WoIOSensor'].connectionType === 'OpenAPI' || model.options.deviceConfig['WoIOSensor'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoIOSensor'] && !model.options.deviceConfig['WoIOSensor'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoIOSensor'] && !model.options.deviceConfig['WoIOSensor'].hide_device && (model.options.deviceConfig['WoIOSensor'].connectionType === 'BLE' || model.options.deviceConfig['WoIOSensor'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoIOSensor'] && !model.options.deviceConfig['WoIOSensor'].hide_device && (model.options.deviceConfig['WoIOSensor'].connectionType === 'OpenAPI' || model.options.deviceConfig['WoIOSensor'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoIOSensor'] && !model.options.deviceConfig['WoIOSensor'].hide_device && (model.options.deviceConfig['WoIOSensor'].connectionType === 'OpenAPI' || model.options.deviceConfig['WoIOSensor'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoIOSensor'] && !model.options.deviceConfig['WoIOSensor'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoIOSensor'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoIOSensor'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoIOSensor'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoIOSensor'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoIOSensor'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoIOSensor'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoIOSensor'].hide_device);" + } + } + } + }, + "Water Detector": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Water Detectors", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "hide_leak": { + "title": "Hide Water Detector's Leak Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.deviceConfig['Water Detector'].hide_device && model.options.deviceConfig['Water Detector'].configDeviceType === 'Water Detector' && model.options.deviceConfig['Water Detector'].deviceId);" + } + }, + "dry": { + "title": "Water Detector's Dry Alert", + "type": "boolean", + "description": "If true, the Water Detector will send an alert when it is dry.", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.deviceConfig['Water Detector'].hide_device && model.options.deviceConfig['Water Detector'].configDeviceType === 'Water Detector' && model.options.deviceConfig['Water Detector'].deviceId);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device && (model.options.deviceConfig['Water Detector'].connectionType === 'BLE' || model.options.deviceConfig['Water Detector'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Water Detectors", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device && (model.options.deviceConfig['Water Detector'].connectionType === 'OpenAPI' || model.options.deviceConfig['Water Detector'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device && (model.options.deviceConfig['Water Detector'].connectionType === 'BLE' || model.options.deviceConfig['Water Detector'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device && (model.options.deviceConfig['Water Detector'].connectionType === 'OpenAPI' || model.options.deviceConfig['Water Detector'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device && (model.options.deviceConfig['Water Detector'].connectionType === 'OpenAPI' || model.options.deviceConfig['Water Detector'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Water Detector'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Water Detector'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Water Detector'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Water Detector'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Water Detector'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Water Detector'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Water Detector'].hide_device);" + } + } + } + }, + "Motion Sensor": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Motion Sensors", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "hide_lightsensor": { + "title": "Hide Light Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Motion Sensor'].hide_device);" + } + }, + "set_minlux": { + "title": "Set Min Lux", + "type": "number", + "placeholder": "1", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Motion Sensor'].hide_device);" + } + }, + "set_maxlux": { + "title": "Set Max Lux", + "type": "number", + "placeholder": "6001", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Motion Sensor'].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device && (model.options.deviceConfig['Water Detector'].connectionType === 'BLE' || model.options.deviceConfig['Water Detector'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Motion Sensors", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device && (model.options.deviceConfig['Water Detector'].connectionType === 'OpenAPI' || model.options.deviceConfig['Water Detector'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device && (model.options.deviceConfig['Water Detector'].connectionType === 'BLE' || model.options.deviceConfig['Water Detector'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device && (model.options.deviceConfig['Water Detector'].connectionType === 'OpenAPI' || model.options.deviceConfig['Water Detector'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device && (model.options.deviceConfig['Water Detector'].connectionType === 'OpenAPI' || model.options.deviceConfig['Water Detector'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Water Detector'] && !model.options.deviceConfig['Water Detector'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Water Detector'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Water Detector'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Water Detector'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Water Detector'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Water Detector'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Water Detector'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Water Detector'].hide_device);" + } + } + } + }, + "Contact Sensor": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Contact Sensors", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "hide_lightsensor": { + "title": "Hide Light Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Contact Sensor'].hide_device);" + } + }, + "set_minlux": { + "title": "Set Min Lux", + "type": "number", + "placeholder": "1", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Contact Sensor'].hide_device);" + } + }, + "set_maxlux": { + "title": "Set Max Lux", + "type": "number", + "placeholder": "6001", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Contact Sensor'].hide_device);" + } + }, + "hide_motionsensor": { + "title": "Hide Contact Sensor's Motion Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.deviceConfig['Contact Sensor'].hide_device && model.options.deviceConfig['Contact Sensor'].configDeviceType === 'Contact Sensor' && model.options.deviceConfig['Contact Sensor'].deviceId);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Contact Sensor'] && !model.options.deviceConfig['Contact Sensor'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Contact Sensor'] && !model.options.deviceConfig['Contact Sensor'].hide_device && (model.options.deviceConfig['Contact Sensor'].connectionType === 'BLE' || model.options.deviceConfig['Contact Sensor'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Contact Sensors", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Contact Sensor'] && !model.options.deviceConfig['Contact Sensor'].hide_device && (model.options.deviceConfig['Contact Sensor'].connectionType === 'OpenAPI' || model.options.deviceConfig['Contact Sensor'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Contact Sensor'] && !model.options.deviceConfig['Contact Sensor'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Contact Sensor'] && !model.options.deviceConfig['Contact Sensor'].hide_device && (model.options.deviceConfig['Contact Sensor'].connectionType === 'BLE' || model.options.deviceConfig['Contact Sensor'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Contact Sensor'] && !model.options.deviceConfig['Contact Sensor'].hide_device && (model.options.deviceConfig['Contact Sensor'].connectionType === 'OpenAPI' || model.options.deviceConfig['Contact Sensor'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Contact Sensor'] && !model.options.deviceConfig['Contact Sensor'].hide_device && (model.options.deviceConfig['Contact Sensor'].connectionType === 'OpenAPI' || model.options.deviceConfig['Contact Sensor'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Contact Sensor'] && !model.options.deviceConfig['Contact Sensor'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Contact Sensor'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Contact Sensor'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Contact Sensor'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Contact Sensor'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Contact Sensor'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Contact Sensor'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Contact Sensor'].hide_device);" + } + } + } + }, + "Curtain": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Curtains", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "disable_group": { + "title": "Disable Grouping", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "hide_lightsensor": { + "title": "Hide Light Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "set_minlux": { + "title": "Set Min Lux", + "type": "number", + "placeholder": "1", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "set_maxlux": { + "title": "Set Max Lux", + "type": "number", + "placeholder": "6001", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "set_min": { + "title": "Set Minimum Open State", + "type": "number", + "placeholder": "0", + "description": "Sets the minimum percentage before needed to set to Open", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "set_max": { + "title": "Set Maximum Close State", + "type": "number", + "placeholder": "100", + "description": "Sets the maximum percentage before needing to set to Closed", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "set_minStep": { + "title": "Set Minimum Step", + "type": "number", + "placeholder": "1", + "description": "Sets the minimum steps device will take. So if set to 20, it would allow you to set the curtain state to 0, 20, 40, 60, 80, and 100", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "setOpenMode": { + "title": "Opening Mode", + "type": "string", + "oneOf": [ + { + "enum": [ + "0" + ], + "title": "Performance Mode" + }, + { + "enum": [ + "1" + ], + "title": "Silent Mode" + } + ], + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "setCloseMode": { + "title": "Closing Mode", + "type": "string", + "oneOf": [ + { + "enum": [ + "0" + ], + "title": "Performance Mode" + }, + { + "enum": [ + "1" + ], + "title": "Silent Mode" + } + ], + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "silentModeSwitch": { + "title": "Enable Silent Mode Switches", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain'] && !model.options.deviceConfig['Curtain'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain'] && !model.options.deviceConfig['Curtain'].hide_device && (model.options.deviceConfig['Curtain'].connectionType === 'BLE' || model.options.deviceConfig['Curtain'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Curtains", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain'] && !model.options.deviceConfig['Curtain'].hide_device && (model.options.deviceConfig['Curtain'].connectionType === 'OpenAPI' || model.options.deviceConfig['Curtain'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain'] && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain'] && !model.options.deviceConfig['Curtain'].hide_device && (model.options.deviceConfig['Curtain'].connectionType === 'BLE' || model.options.deviceConfig['Curtain'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain'] && !model.options.deviceConfig['Curtain'].hide_device && (model.options.deviceConfig['Curtain'].connectionType === 'OpenAPI' || model.options.deviceConfig['Curtain'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain'] && !model.options.deviceConfig['Curtain'].hide_device && (model.options.deviceConfig['Curtain'].connectionType === 'OpenAPI' || model.options.deviceConfig['Curtain'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain'] && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain'].hide_device);" + } + } + } + }, + "Curtain3": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Curtain 3s", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "disable_group": { + "title": "Disable Grouping", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "hide_lightsensor": { + "title": "Hide Light Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "set_minlux": { + "title": "Set Min Lux", + "type": "number", + "placeholder": "1", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "set_maxlux": { + "title": "Set Max Lux", + "type": "number", + "placeholder": "6001", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "set_min": { + "title": "Set Minimum Open State", + "type": "number", + "placeholder": "0", + "description": "Sets the minimum percentage before needed to set to Open", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "set_max": { + "title": "Set Maximum Close State", + "type": "number", + "placeholder": "100", + "description": "Sets the maximum percentage before needing to set to Closed", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "set_minStep": { + "title": "Set Minimum Step", + "type": "number", + "placeholder": "1", + "description": "Sets the minimum steps device will take. So if set to 20, it would allow you to set the curtain state to 0, 20, 40, 60, 80, and 100", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "setOpenMode": { + "title": "Opening Mode", + "type": "string", + "oneOf": [ + { + "enum": [ + "0" + ], + "title": "Performance Mode" + }, + { + "enum": [ + "1" + ], + "title": "Silent Mode" + } + ], + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "setCloseMode": { + "title": "Closing Mode", + "type": "string", + "oneOf": [ + { + "enum": [ + "0" + ], + "title": "Performance Mode" + }, + { + "enum": [ + "1" + ], + "title": "Silent Mode" + } + ], + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "silentModeSwitch": { + "title": "Enable Silent Mode Switches", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain3'] && !model.options.deviceConfig['Curtain3'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain3'] && !model.options.deviceConfig['Curtain3'].hide_device && (model.options.deviceConfig['Curtain3'].connectionType === 'BLE' || model.options.deviceConfig['Curtain3'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Curtain3s", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain3'] && !model.options.deviceConfig['Curtain3'].hide_device && (model.options.deviceConfig['Curtain3'].connectionType === 'OpenAPI' || model.options.deviceConfig['Curtain3'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain3'] && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain3'] && !model.options.deviceConfig['Curtain3'].hide_device && (model.options.deviceConfig['Curtain3'].connectionType === 'BLE' || model.options.deviceConfig['Curtain3'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain3'] && !model.options.deviceConfig['Curtain3'].hide_device && (model.options.deviceConfig['Curtain3'].connectionType === 'OpenAPI' || model.options.deviceConfig['Curtain3'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain3'] && !model.options.deviceConfig['Curtain3'].hide_device && (model.options.deviceConfig['Curtain3'].connectionType === 'OpenAPI' || model.options.deviceConfig['Curtain3'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Curtain3'] && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Curtain3'].hide_device);" + } + } + } + }, + "WoRollerShade": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All WoRollerShades", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "disable_group": { + "title": "Disable Grouping", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "hide_lightsensor": { + "title": "Hide Light Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "set_minlux": { + "title": "Set Min Lux", + "type": "number", + "placeholder": "1", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "set_maxlux": { + "title": "Set Max Lux", + "type": "number", + "placeholder": "6001", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "set_min": { + "title": "Set Minimum Open State", + "type": "number", + "placeholder": "0", + "description": "Sets the minimum percentage before needed to set to Open", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "set_max": { + "title": "Set Maximum Close State", + "type": "number", + "placeholder": "100", + "description": "Sets the maximum percentage before needing to set to Closed", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "set_minStep": { + "title": "Set Minimum Step", + "type": "number", + "placeholder": "1", + "description": "Sets the minimum steps device will take. So if set to 20, it would allow you to set the curtain state to 0, 20, 40, 60, 80, and 100", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "setOpenMode": { + "title": "Opening Mode", + "type": "string", + "oneOf": [ + { + "enum": [ + "0" + ], + "title": "Performance Mode" + }, + { + "enum": [ + "1" + ], + "title": "Silent Mode" + } + ], + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "setCloseMode": { + "title": "Closing Mode", + "type": "string", + "oneOf": [ + { + "enum": [ + "0" + ], + "title": "Performance Mode" + }, + { + "enum": [ + "1" + ], + "title": "Silent Mode" + } + ], + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "silentModeSwitch": { + "title": "Enable Silent Mode Switches", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoRollerShade'] && !model.options.deviceConfig['WoRollerShade'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoRollerShade'] && !model.options.deviceConfig['WoRollerShade'].hide_device && (model.options.deviceConfig['WoRollerShade'].connectionType === 'BLE' || model.options.deviceConfig['WoRollerShade'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all WoRollerShades", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoRollerShade'] && !model.options.deviceConfig['WoRollerShade'].hide_device && (model.options.deviceConfig['WoRollerShade'].connectionType === 'OpenAPI' || model.options.deviceConfig['WoRollerShade'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoRollerShade'] && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoRollerShade'] && !model.options.deviceConfig['WoRollerShade'].hide_device && (model.options.deviceConfig['WoRollerShade'].connectionType === 'BLE' || model.options.deviceConfig['WoRollerShade'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoRollerShade'] && !model.options.deviceConfig['WoRollerShade'].hide_device && (model.options.deviceConfig['WoRollerShade'].connectionType === 'OpenAPI' || model.options.deviceConfig['WoRollerShade'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoRollerShade'] && !model.options.deviceConfig['WoRollerShade'].hide_device && (model.options.deviceConfig['WoRollerShade'].connectionType === 'OpenAPI' || model.options.deviceConfig['WoRollerShade'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoRollerShade'] && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoRollerShade'].hide_device);" + } + } + } + }, + "Roller Shade": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Roller Shades", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "disable_group": { + "title": "Disable Grouping", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "hide_lightsensor": { + "title": "Hide Light Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "set_minlux": { + "title": "Set Min Lux", + "type": "number", + "placeholder": "1", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "set_maxlux": { + "title": "Set Max Lux", + "type": "number", + "placeholder": "6001", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "set_min": { + "title": "Set Minimum Open State", + "type": "number", + "placeholder": "0", + "description": "Sets the minimum percentage before needed to set to Open", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "set_max": { + "title": "Set Maximum Close State", + "type": "number", + "placeholder": "100", + "description": "Sets the maximum percentage before needing to set to Closed", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "set_minStep": { + "title": "Set Minimum Step", + "type": "number", + "placeholder": "1", + "description": "Sets the minimum steps device will take. So if set to 20, it would allow you to set the curtain state to 0, 20, 40, 60, 80, and 100", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "setOpenMode": { + "title": "Opening Mode", + "type": "string", + "oneOf": [ + { + "enum": [ + "0" + ], + "title": "Performance Mode" + }, + { + "enum": [ + "1" + ], + "title": "Silent Mode" + } + ], + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "setCloseMode": { + "title": "Closing Mode", + "type": "string", + "oneOf": [ + { + "enum": [ + "0" + ], + "title": "Performance Mode" + }, + { + "enum": [ + "1" + ], + "title": "Silent Mode" + } + ], + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "silentModeSwitch": { + "title": "Enable Silent Mode Switches", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Roller Shade'] && !model.options.deviceConfig['Roller Shade'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Roller Shade'] && !model.options.deviceConfig['Roller Shade'].hide_device && (model.options.deviceConfig['Roller Shade'].connectionType === 'BLE' || model.options.deviceConfig['Roller Shade'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Roller Shades", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Roller Shade'] && !model.options.deviceConfig['Roller Shade'].hide_device && (model.options.deviceConfig['Roller Shade'].connectionType === 'OpenAPI' || model.options.deviceConfig['Roller Shade'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Roller Shade'] && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Roller Shade'] && !model.options.deviceConfig['Roller Shade'].hide_device && (model.options.deviceConfig['Roller Shade'].connectionType === 'BLE' || model.options.deviceConfig['Roller Shade'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Roller Shade'] && !model.options.deviceConfig['Roller Shade'].hide_device && (model.options.deviceConfig['Roller Shade'].connectionType === 'OpenAPI' || model.options.deviceConfig['Roller Shade'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Roller Shade'] && !model.options.deviceConfig['Roller Shade'].hide_device && (model.options.deviceConfig['Roller Shade'].connectionType === 'OpenAPI' || model.options.deviceConfig['Roller Shade'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Roller Shade'] && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Roller Shade'].hide_device);" + } + } + } + }, + "Blind Tilt": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Blind Tilts", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "disable_group": { + "title": "Disable Grouping", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "mapping": { + "title": "Mapping Mode", + "type": "string", + "required": true, + "default": "", + "oneOf": [ + { + "title": "Default", + "enum": [""] + }, + { + "title": "Down and Up", + "enum": ["down_and_up"] + }, + { + "title": "Only Down", + "enum": ["only_down"] + }, + { + "title": "Only Up", + "enum": ["only_up"] + }, + { + "title": "Up and Down", + "enum": ["up_and_down"] + }, + { + "title": "Use Tilt for Direction", + "enum": ["use_tilt_for_direction"] + } + ], + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "hide_lightsensor": { + "title": "Hide Light Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "set_minlux": { + "title": "Set Min Lux", + "type": "number", + "placeholder": "1", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "set_maxlux": { + "title": "Set Max Lux", + "type": "number", + "placeholder": "6001", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "set_min": { + "title": "Set Minimum Open State", + "type": "number", + "placeholder": "0", + "description": "Sets the minimum percentage before needed to set to Open", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "set_max": { + "title": "Set Maximum Close State", + "type": "number", + "placeholder": "100", + "description": "Sets the maximum percentage before needing to set to Closed", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "set_minStep": { + "title": "Set Minimum Step", + "type": "number", + "placeholder": "1", + "description": "Sets the minimum steps device will take. So if set to 20, it would allow you to set the curtain state to 0, 20, 40, 60, 80, and 100", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "setOpenMode": { + "title": "Opening Mode", + "type": "string", + "oneOf": [ + { + "enum": [ + "0" + ], + "title": "Performance Mode" + }, + { + "enum": [ + "1" + ], + "title": "Silent Mode" + } + ], + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "setCloseMode": { + "title": "Closing Mode", + "type": "string", + "oneOf": [ + { + "enum": [ + "0" + ], + "title": "Performance Mode" + }, + { + "enum": [ + "1" + ], + "title": "Silent Mode" + } + ], + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "silentModeSwitch": { + "title": "Enable Silent Mode Switches", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Blind Tilt'] && !model.options.deviceConfig['Blind Tilt'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Blind Tilt'] && !model.options.deviceConfig['Blind Tilt'].hide_device && (model.options.deviceConfig['Blind Tilt'].connectionType === 'BLE' || model.options.deviceConfig['Blind Tilt'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Blind Tilts", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Blind Tilt'] && !model.options.deviceConfig['Blind Tilt'].hide_device && (model.options.deviceConfig['Blind Tilt'].connectionType === 'OpenAPI' || model.options.deviceConfig['Blind Tilt'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Blind Tilt'] && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Blind Tilt'] && !model.options.deviceConfig['Blind Tilt'].hide_device && (model.options.deviceConfig['Blind Tilt'].connectionType === 'BLE' || model.options.deviceConfig['Blind Tilt'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Blind Tilt'] && !model.options.deviceConfig['Blind Tilt'].hide_device && (model.options.deviceConfig['Blind Tilt'].connectionType === 'OpenAPI' || model.options.deviceConfig['Blind Tilt'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Blind Tilt'] && !model.options.deviceConfig['Blind Tilt'].hide_device && (model.options.deviceConfig['Blind Tilt'].connectionType === 'OpenAPI' || model.options.deviceConfig['Blind Tilt'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Blind Tilt'] && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Blind Tilt'].hide_device);" + } + } + } + }, + "Plug": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Plugs", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug'] && !model.options.deviceConfig['Plug'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug'] && !model.options.deviceConfig['Plug'].hide_device && (model.options.deviceConfig['Plug'].connectionType === 'BLE' || model.options.deviceConfig['Plug'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Plugs", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug'] && !model.options.deviceConfig['Plug'].hide_device && (model.options.deviceConfig['Plug'].connectionType === 'OpenAPI' || model.options.deviceConfig['Plug'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug'] && !model.options.deviceConfig['Plug'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug'] && !model.options.deviceConfig['Plug'].hide_device && (model.options.deviceConfig['Plug'].connectionType === 'BLE' || model.options.deviceConfig['Plug'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug'] && !model.options.deviceConfig['Plug'].hide_device && (model.options.deviceConfig['Plug'].connectionType === 'OpenAPI' || model.options.deviceConfig['Plug'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug'] && !model.options.deviceConfig['Plug'].hide_device && (model.options.deviceConfig['Plug'].connectionType === 'OpenAPI' || model.options.deviceConfig['Plug'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug'] && !model.options.deviceConfig['Plug'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug'].hide_device);" + } + } + } + }, + "Plug Mini (US)": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Plug Mini (US)s", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (US)'] && !model.options.deviceConfig['Plug Mini (US)'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (US)'] && !model.options.deviceConfig['Plug Mini (US)'].hide_device && (model.options.deviceConfig['Plug Mini (US)'].connectionType === 'BLE' || model.options.deviceConfig['Plug Mini (US)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Plug Mini (US)s", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (US)'] && !model.options.deviceConfig['Plug Mini (US)'].hide_device && (model.options.deviceConfig['Plug Mini (US)'].connectionType === 'OpenAPI' || model.options.deviceConfig['Plug Mini (US)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (US)'] && !model.options.deviceConfig['Plug Mini (US)'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (US)'] && !model.options.deviceConfig['Plug Mini (US)'].hide_device && (model.options.deviceConfig['Plug Mini (US)'].connectionType === 'BLE' || model.options.deviceConfig['Plug Mini (US)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (US)'] && !model.options.deviceConfig['Plug Mini (US)'].hide_device && (model.options.deviceConfig['Plug Mini (US)'].connectionType === 'OpenAPI' || model.options.deviceConfig['Plug Mini (US)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (US)'] && !model.options.deviceConfig['Plug Mini (US)'].hide_device && (model.options.deviceConfig['Plug Mini (US)'].connectionType === 'OpenAPI' || model.options.deviceConfig['Plug Mini (US)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (US)'] && !model.options.deviceConfig['Plug Mini (US)'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug Mini (US)'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug Mini (US)'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug Mini (US)'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug Mini (US)'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug Mini (US)'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug Mini (US)'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug Mini (US)'].hide_device);" + } + } + } + }, + "Plug Mini (JP)": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Plug Mini (JP)s", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (JP)'] && !model.options.deviceConfig['Plug Mini (JP)'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (JP)'] && !model.options.deviceConfig['Plug Mini (JP)'].hide_device && (model.options.deviceConfig['Plug Mini (JP)'].connectionType === 'BLE' || model.options.deviceConfig['Plug Mini (JP)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Plug Mini (JP)s", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (JP)'] && !model.options.deviceConfig['Plug Mini (JP)'].hide_device && (model.options.deviceConfig['Plug Mini (JP)'].connectionType === 'OpenAPI' || model.options.deviceConfig['Plug Mini (JP)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (JP)'] && !model.options.deviceConfig['Plug Mini (JP)'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (JP)'] && !model.options.deviceConfig['Plug Mini (JP)'].hide_device && (model.options.deviceConfig['Plug Mini (JP)'].connectionType === 'BLE' || model.options.deviceConfig['Plug Mini (JP)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (JP)'] && !model.options.deviceConfig['Plug Mini (JP)'].hide_device && (model.options.deviceConfig['Plug Mini (JP)'].connectionType === 'OpenAPI' || model.options.deviceConfig['Plug Mini (JP)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (JP)'] && !model.options.deviceConfig['Plug Mini (JP)'].hide_device && (model.options.deviceConfig['Plug Mini (JP)'].connectionType === 'OpenAPI' || model.options.deviceConfig['Plug Mini (JP)'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Plug Mini (JP)'] && !model.options.deviceConfig['Plug Mini (JP)'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug Mini (JP)'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug Mini (JP)'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug Mini (JP)'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug Mini (JP)'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug Mini (JP)'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug Mini (JP)'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Plug Mini (JP)'].hide_device);" + } + } + } + }, + "Smart Lock": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Smart Locks", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "hide_contactsensor": { + "title": "Hide Lock's Contact Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.deviceConfig['Smart Lock'].hide_device && (model.options.deviceConfig['Smart Lock'].configDeviceType === 'Smart Lock' || model.options.deviceConfig['Smart Lock'].configDeviceType === 'Smart Lock Pro') && model.options.deviceConfig['Smart Lock'].deviceId);" + } + }, + "activate_latchbutton": { + "title": "Activate Latch Button", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.deviceConfig['Smart Lock'].hide_device && (model.options.deviceConfig['Smart Lock'].configDeviceType === 'Smart Lock' || model.options.deviceConfig['Smart Lock'].configDeviceType === 'Smart Lock Pro') && model.options.deviceConfig['Smart Lock'].deviceId);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock'] && !model.options.deviceConfig['Smart Lock'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock'] && !model.options.deviceConfig['Smart Lock'].hide_device && (model.options.deviceConfig['Smart Lock'].connectionType === 'BLE' || model.options.deviceConfig['Smart Lock'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Smart Locks", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock'] && !model.options.deviceConfig['Smart Lock'].hide_device && (model.options.deviceConfig['Smart Lock'].connectionType === 'OpenAPI' || model.options.deviceConfig['Smart Lock'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock'] && !model.options.deviceConfig['Smart Lock'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock'] && !model.options.deviceConfig['Smart Lock'].hide_device && (model.options.deviceConfig['Smart Lock'].connectionType === 'BLE' || model.options.deviceConfig['Smart Lock'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock'] && !model.options.deviceConfig['Smart Lock'].hide_device && (model.options.deviceConfig['Smart Lock'].connectionType === 'OpenAPI' || model.options.deviceConfig['Smart Lock'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock'] && !model.options.deviceConfig['Smart Lock'].hide_device && (model.options.deviceConfig['Smart Lock'].connectionType === 'OpenAPI' || model.options.deviceConfig['Smart Lock'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock'] && !model.options.deviceConfig['Smart Lock'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Smart Lock'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Smart Lock'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Smart Lock'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Smart Lock'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Smart Lock'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Smart Lock'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Smart Lock'].hide_device);" + } + } + } + }, + "Smart Lock Pro": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Smart Lock Pros", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "hide_contactsensor": { + "title": "Hide Lock's Contact Sensor", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.deviceConfig['Smart Lock Pro'].hide_device && (model.options.deviceConfig['Smart Lock Pro'].configDeviceType === 'Smart Lock' || model.options.deviceConfig['Smart Lock Pro'].configDeviceType === 'Smart Lock Pro') && model.options.deviceConfig['Smart Lock Pro'].deviceId);" + } + }, + "activate_latchbutton": { + "title": "Activate Latch Button", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.deviceConfig['Smart Lock Pro'].hide_device && (model.options.deviceConfig['Smart Lock Pro'].configDeviceType === 'Smart Lock' || model.options.deviceConfig['Smart Lock Pro'].configDeviceType === 'Smart Lock Pro') && model.options.deviceConfig['Smart Lock Pro'].deviceId);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock Pro'] && !model.options.deviceConfig['Smart Lock Pro'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock Pro'] && !model.options.deviceConfig['Smart Lock Pro'].hide_device && (model.options.deviceConfig['Smart Lock Pro'].connectionType === 'BLE' || model.options.deviceConfig['Smart Lock Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Smart Lock Pros", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock Pro'] && !model.options.deviceConfig['Smart Lock Pro'].hide_device && (model.options.deviceConfig['Smart Lock Pro'].connectionType === 'OpenAPI' || model.options.deviceConfig['Smart Lock Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock Pro'] && !model.options.deviceConfig['Smart Lock Pro'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock Pro'] && !model.options.deviceConfig['Smart Lock Pro'].hide_device && (model.options.deviceConfig['Smart Lock Pro'].connectionType === 'BLE' || model.options.deviceConfig['Smart Lock Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock Pro'] && !model.options.deviceConfig['Smart Lock Pro'].hide_device && (model.options.deviceConfig['Smart Lock Pro'].connectionType === 'OpenAPI' || model.options.deviceConfig['Smart Lock Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock Pro'] && !model.options.deviceConfig['Smart Lock Pro'].hide_device && (model.options.deviceConfig['Smart Lock Pro'].connectionType === 'OpenAPI' || model.options.deviceConfig['Smart Lock Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Smart Lock Pro'] && !model.options.deviceConfig['Smart Lock Pro'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Smart Lock Pro'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Smart Lock Pro'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Smart Lock Pro'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Smart Lock Pro'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Smart Lock Pro'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Smart Lock Pro'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Smart Lock Pro'].hide_device);" + } + } + } + }, + "Color Bulb": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Color Bulbs", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "set_minStep": { + "title": "Set Minimum Step", + "type": "number", + "placeholder": "1", + "description": "Sets the minimum steps device will take. So if set to 20, it would allow you to set the curtain state to 0, 20, 40, 60, 80, and 100", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Color Bulb'].hide_device);" + } + }, + "adaptiveLightingShift": { + "title": "Adaptive Lighting Shift", + "type": "number", + "placeholder": "-1", + "description": "The mired for each Adaptive Lighting update will be increased by this value, making the light appear warmer. Set to -1 to remove Adaptive Lighting feature. Must be -1 or more.", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.deviceConfig['Color Bulb'].hide_device && (model.options.deviceConfig['Color Bulb'].configDeviceType === 'Color Bulb' || model.options.deviceConfig['Color Bulb'].configDeviceType === 'Strip Light') && model.options.deviceConfig['Color Bulb'].deviceId);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Color Bulb'] && !model.options.deviceConfig['Color Bulb'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Color Bulb'] && !model.options.deviceConfig['Color Bulb'].hide_device && (model.options.deviceConfig['Color Bulb'].connectionType === 'BLE' || model.options.deviceConfig['Color Bulb'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Color Bulbs", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Color Bulb'] && !model.options.deviceConfig['Color Bulb'].hide_device && (model.options.deviceConfig['Color Bulb'].connectionType === 'OpenAPI' || model.options.deviceConfig['Color Bulb'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Color Bulb'] && !model.options.deviceConfig['Color Bulb'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Color Bulb'] && !model.options.deviceConfig['Color Bulb'].hide_device && (model.options.deviceConfig['Color Bulb'].connectionType === 'BLE' || model.options.deviceConfig['Color Bulb'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Color Bulb'] && !model.options.deviceConfig['Color Bulb'].hide_device && (model.options.deviceConfig['Color Bulb'].connectionType === 'OpenAPI' || model.options.deviceConfig['Color Bulb'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Color Bulb'] && !model.options.deviceConfig['Color Bulb'].hide_device && (model.options.deviceConfig['Color Bulb'].connectionType === 'OpenAPI' || model.options.deviceConfig['Color Bulb'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Color Bulb'] && !model.options.deviceConfig['Color Bulb'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Color Bulb'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Color Bulb'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Color Bulb'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Color Bulb'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Color Bulb'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Color Bulb'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Color Bulb'].hide_device);" + } + } + } + }, + "K10+": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All K10+s", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+'] && !model.options.deviceConfig['K10+'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+'] && !model.options.deviceConfig['K10+'].hide_device && (model.options.deviceConfig['K10+'].connectionType === 'BLE' || model.options.deviceConfig['K10+'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all K10+s", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+'] && !model.options.deviceConfig['K10+'].hide_device && (model.options.deviceConfig['K10+'].connectionType === 'OpenAPI' || model.options.deviceConfig['K10+'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+'] && !model.options.deviceConfig['K10+'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+'] && !model.options.deviceConfig['K10+'].hide_device && (model.options.deviceConfig['K10+'].connectionType === 'BLE' || model.options.deviceConfig['K10+'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+'] && !model.options.deviceConfig['K10+'].hide_device && (model.options.deviceConfig['K10+'].connectionType === 'OpenAPI' || model.options.deviceConfig['K10+'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+'] && !model.options.deviceConfig['K10+'].hide_device && (model.options.deviceConfig['K10+'].connectionType === 'OpenAPI' || model.options.deviceConfig['K10+'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+'] && !model.options.deviceConfig['K10+'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['K10+'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['K10+'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['K10+'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['K10+'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['K10+'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['K10+'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['K10+'].hide_device);" + } + } + } + }, + "K10+ Pro": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All K10+ Pros", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+ Pro'] && !model.options.deviceConfig['K10+ Pro'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+ Pro'] && !model.options.deviceConfig['K10+ Pro'].hide_device && (model.options.deviceConfig['K10+ Pro'].connectionType === 'BLE' || model.options.deviceConfig['K10+ Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all K10+ Pros", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+ Pro'] && !model.options.deviceConfig['K10+ Pro'].hide_device && (model.options.deviceConfig['K10+ Pro'].connectionType === 'OpenAPI' || model.options.deviceConfig['K10+ Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+ Pro'] && !model.options.deviceConfig['K10+ Pro'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+ Pro'] && !model.options.deviceConfig['K10+ Pro'].hide_device && (model.options.deviceConfig['K10+ Pro'].connectionType === 'BLE' || model.options.deviceConfig['K10+ Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+ Pro'] && !model.options.deviceConfig['K10+ Pro'].hide_device && (model.options.deviceConfig['K10+ Pro'].connectionType === 'OpenAPI' || model.options.deviceConfig['K10+ Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+ Pro'] && !model.options.deviceConfig['K10+ Pro'].hide_device && (model.options.deviceConfig['K10+ Pro'].connectionType === 'OpenAPI' || model.options.deviceConfig['K10+ Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['K10+ Pro'] && !model.options.deviceConfig['K10+ Pro'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['K10+ Pro'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['K10+ Pro'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['K10+ Pro'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['K10+ Pro'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['K10+ Pro'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['K10+ Pro'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['K10+ Pro'].hide_device);" + } + } + } + }, + "WoSweeper": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All WoSweepers", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeper'] && !model.options.deviceConfig['WoSweeper'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeper'] && !model.options.deviceConfig['WoSweeper'].hide_device && (model.options.deviceConfig['WoSweeper'].connectionType === 'BLE' || model.options.deviceConfig['WoSweeper'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all WoSweepers", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeper'] && !model.options.deviceConfig['WoSweeper'].hide_device && (model.options.deviceConfig['WoSweeper'].connectionType === 'OpenAPI' || model.options.deviceConfig['WoSweeper'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeper'] && !model.options.deviceConfig['WoSweeper'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeper'] && !model.options.deviceConfig['WoSweeper'].hide_device && (model.options.deviceConfig['WoSweeper'].connectionType === 'BLE' || model.options.deviceConfig['WoSweeper'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeper'] && !model.options.deviceConfig['WoSweeper'].hide_device && (model.options.deviceConfig['WoSweeper'].connectionType === 'OpenAPI' || model.options.deviceConfig['WoSweeper'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeper'] && !model.options.deviceConfig['WoSweeper'].hide_device && (model.options.deviceConfig['WoSweeper'].connectionType === 'OpenAPI' || model.options.deviceConfig['WoSweeper'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeper'] && !model.options.deviceConfig['WoSweeper'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoSweeper'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoSweeper'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoSweeper'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoSweeper'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoSweeper'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoSweeper'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoSweeper'].hide_device);" + } + } + } + }, + "WoSweeperMini": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All WoSweeper Minis", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeperMini'] && !model.options.deviceConfig['WoSweeperMini'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeperMini'] && !model.options.deviceConfig['WoSweeperMini'].hide_device && (model.options.deviceConfig['WoSweeperMini'].connectionType === 'BLE' || model.options.deviceConfig['WoSweeperMini'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all WoSweeperMinis", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeperMini'] && !model.options.deviceConfig['WoSweeperMini'].hide_device && (model.options.deviceConfig['WoSweeperMini'].connectionType === 'OpenAPI' || model.options.deviceConfig['WoSweeperMini'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeperMini'] && !model.options.deviceConfig['WoSweeperMini'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeperMini'] && !model.options.deviceConfig['WoSweeperMini'].hide_device && (model.options.deviceConfig['WoSweeperMini'].connectionType === 'BLE' || model.options.deviceConfig['WoSweeperMini'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeperMini'] && !model.options.deviceConfig['WoSweeperMini'].hide_device && (model.options.deviceConfig['WoSweeperMini'].connectionType === 'OpenAPI' || model.options.deviceConfig['WoSweeperMini'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeperMini'] && !model.options.deviceConfig['WoSweeperMini'].hide_device && (model.options.deviceConfig['WoSweeperMini'].connectionType === 'OpenAPI' || model.options.deviceConfig['WoSweeperMini'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['WoSweeperMini'] && !model.options.deviceConfig['WoSweeperMini'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoSweeperMini'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoSweeperMini'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoSweeperMini'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoSweeperMini'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoSweeperMini'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoSweeperMini'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['WoSweeperMini'].hide_device);" + } + } + } + }, + "Robot Vacuum Cleaner S1": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Robot Vacuum Cleaner S1s", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S1'].connectionType === 'BLE' || model.options.deviceConfig['Robot Vacuum Cleaner S1'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Robot Vacuum Cleaner S1 Pluses", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S1'].connectionType === 'OpenAPI' || model.options.deviceConfig['Robot Vacuum Cleaner S1'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S1'].connectionType === 'BLE' || model.options.deviceConfig['Robot Vacuum Cleaner S1'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S1'].connectionType === 'OpenAPI' || model.options.deviceConfig['Robot Vacuum Cleaner S1'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S1'].connectionType === 'OpenAPI' || model.options.deviceConfig['Robot Vacuum Cleaner S1'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device);" + } + } + } + }, + "Robot Vacuum Cleaner S1 Plus": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Robot Vacuum Cleaner S1 Pluses", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].connectionType === 'BLE' || model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Robot Vacuum Cleaner S1 Pluses", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].connectionType === 'OpenAPI' || model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].connectionType === 'BLE' || model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].connectionType === 'OpenAPI' || model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].connectionType === 'OpenAPI' || model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'] && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device);" + } + } + } + }, + "Robot Vacuum Cleaner S10": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Robot Vacuum Cleaner S10s", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S10'] && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S10'] && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S10'].connectionType === 'BLE' || model.options.deviceConfig['Robot Vacuum Cleaner S10'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Robot Vacuum Cleaner S10s", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S10'] && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S10'].connectionType === 'OpenAPI' || model.options.deviceConfig['Robot Vacuum Cleaner S10'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S10'] && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S10'] && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S10'].connectionType === 'BLE' || model.options.deviceConfig['Robot Vacuum Cleaner S10'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S10'] && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S10'].connectionType === 'OpenAPI' || model.options.deviceConfig['Robot Vacuum Cleaner S10'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S10'] && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device && (model.options.deviceConfig['Robot Vacuum Cleaner S10'].connectionType === 'OpenAPI' || model.options.deviceConfig['Robot Vacuum Cleaner S10'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Robot Vacuum Cleaner S10'] && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device);" + } + } + } + }, + "Ceiling Light": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Ceiling Lights", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "set_minStep": { + "title": "Set Minimum Step", + "type": "number", + "placeholder": "1", + "description": "Sets the minimum steps device will take. So if set to 20, it would allow you to set the curtain state to 0, 20, 40, 60, 80, and 100", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light'].hide_device);" + } + }, + "adaptiveLightingShift": { + "title": "Adaptive Lighting Shift", + "type": "number", + "placeholder": "-1", + "description": "The mired for each Adaptive Lighting update will be increased by this value, making the light appear warmer. Set to -1 to remove Adaptive Lighting feature. Must be -1 or more.", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.deviceConfig['Ceiling Light'].hide_device && (model.options.deviceConfig['Ceiling Light'].configDeviceType === 'Color Bulb' || model.options.deviceConfig['Ceiling Light'].configDeviceType === 'Strip Light') && model.options.deviceConfig['Ceiling Light'].deviceId);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light'] && !model.options.deviceConfig['Ceiling Light'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light'] && !model.options.deviceConfig['Ceiling Light'].hide_device && (model.options.deviceConfig['Ceiling Light'].connectionType === 'BLE' || model.options.deviceConfig['Ceiling Light'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Ceiling Lights", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light'] && !model.options.deviceConfig['Ceiling Light'].hide_device && (model.options.deviceConfig['Ceiling Light'].connectionType === 'OpenAPI' || model.options.deviceConfig['Ceiling Light'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light'] && !model.options.deviceConfig['Ceiling Light'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light'] && !model.options.deviceConfig['Ceiling Light'].hide_device && (model.options.deviceConfig['Ceiling Light'].connectionType === 'BLE' || model.options.deviceConfig['Ceiling Light'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light'] && !model.options.deviceConfig['Ceiling Light'].hide_device && (model.options.deviceConfig['Ceiling Light'].connectionType === 'OpenAPI' || model.options.deviceConfig['Ceiling Light'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light'] && !model.options.deviceConfig['Ceiling Light'].hide_device && (model.options.deviceConfig['Ceiling Light'].connectionType === 'OpenAPI' || model.options.deviceConfig['Ceiling Light'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light'] && !model.options.deviceConfig['Ceiling Light'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light'].hide_device);" + } + } + } + }, + "Ceiling Light Pro": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Ceiling Light Pros", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "set_minStep": { + "title": "Set Minimum Step", + "type": "number", + "placeholder": "1", + "description": "Sets the minimum steps device will take. So if set to 20, it would allow you to set the curtain state to 0, 20, 40, 60, 80, and 100", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light Pro'].hide_device);" + } + }, + "adaptiveLightingShift": { + "title": "Adaptive Lighting Shift", + "type": "number", + "placeholder": "-1", + "description": "The mired for each Adaptive Lighting update will be increased by this value, making the light appear warmer. Set to -1 to remove Adaptive Lighting feature. Must be -1 or more.", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.deviceConfig['Ceiling Light Pro'].hide_device && (model.options.deviceConfig['Ceiling Light Pro'].configDeviceType === 'Color Bulb' || model.options.deviceConfig['Ceiling Light Pro'].configDeviceType === 'Strip Light') && model.options.deviceConfig['Ceiling Light Pro'].deviceId);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light Pro'] && !model.options.deviceConfig['Ceiling Light Pro'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light Pro'] && !model.options.deviceConfig['Ceiling Light Pro'].hide_device && (model.options.deviceConfig['Ceiling Light Pro'].connectionType === 'BLE' || model.options.deviceConfig['Ceiling Light Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Ceiling Light Pros", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light Pro'] && !model.options.deviceConfig['Ceiling Light Pro'].hide_device && (model.options.deviceConfig['Ceiling Light Pro'].connectionType === 'OpenAPI' || model.options.deviceConfig['Ceiling Light Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light Pro'] && !model.options.deviceConfig['Ceiling Light Pro'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light Pro'] && !model.options.deviceConfig['Ceiling Light Pro'].hide_device && (model.options.deviceConfig['Ceiling Light Pro'].connectionType === 'BLE' || model.options.deviceConfig['Ceiling Light Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light Pro'] && !model.options.deviceConfig['Ceiling Light Pro'].hide_device && (model.options.deviceConfig['Ceiling Light Pro'].connectionType === 'OpenAPI' || model.options.deviceConfig['Ceiling Light Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light Pro'] && !model.options.deviceConfig['Ceiling Light Pro'].hide_device && (model.options.deviceConfig['Ceiling Light Pro'].connectionType === 'OpenAPI' || model.options.deviceConfig['Ceiling Light Pro'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Ceiling Light Pro'] && !model.options.deviceConfig['Ceiling Light Pro'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light Pro'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light Pro'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light Pro'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light Pro'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light Pro'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light Pro'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Ceiling Light Pro'].hide_device);" + } + } + } + }, + "Strip Light": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Strip Lights", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "set_minStep": { + "title": "Set Minimum Step", + "type": "number", + "placeholder": "1", + "description": "Sets the minimum steps device will take. So if set to 20, it would allow you to set the curtain state to 0, 20, 40, 60, 80, and 100", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Strip Light'].hide_device);" + } + }, + "adaptiveLightingShift": { + "title": "Adaptive Lighting Shift", + "type": "number", + "placeholder": "-1", + "description": "The mired for each Adaptive Lighting update will be increased by this value, making the light appear warmer. Set to -1 to remove Adaptive Lighting feature. Must be -1 or more.", + "condition": { + "functionBody": "return (model.options && model.options.devices && !model.options.deviceConfig['Strip Light'].hide_device && (model.options.deviceConfig['Strip Light'].configDeviceType === 'Color Bulb' || model.options.deviceConfig['Strip Light'].configDeviceType === 'Strip Light') && model.options.deviceConfig['Strip Light'].deviceId);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Strip Light'] && !model.options.deviceConfig['Strip Light'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Strip Light'] && !model.options.deviceConfig['Strip Light'].hide_device && (model.options.deviceConfig['Strip Light'].connectionType === 'BLE' || model.options.deviceConfig['Strip Light'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Strip Lights", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Strip Light'] && !model.options.deviceConfig['Strip Light'].hide_device && (model.options.deviceConfig['Strip Light'].connectionType === 'OpenAPI' || model.options.deviceConfig['Strip Light'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Strip Light'] && !model.options.deviceConfig['Strip Light'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Strip Light'] && !model.options.deviceConfig['Strip Light'].hide_device && (model.options.deviceConfig['Strip Light'].connectionType === 'BLE' || model.options.deviceConfig['Strip Light'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Strip Light'] && !model.options.deviceConfig['Strip Light'].hide_device && (model.options.deviceConfig['Strip Light'].connectionType === 'OpenAPI' || model.options.deviceConfig['Strip Light'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Strip Light'] && !model.options.deviceConfig['Strip Light'].hide_device && (model.options.deviceConfig['Strip Light'].connectionType === 'OpenAPI' || model.options.deviceConfig['Strip Light'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Strip Light'] && !model.options.deviceConfig['Strip Light'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Strip Light'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Strip Light'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Strip Light'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Strip Light'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Strip Light'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Strip Light'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Strip Light'].hide_device);" + } + } + } + }, + "Battery Circulator Fan": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Battery Circulator Fans", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "set_minStep": { + "title": "Set Minimum Step", + "type": "number", + "placeholder": "1", + "description": "Sets the minimum steps device will take. So if set to 20, it would allow you to set the curtain state to 0, 20, 40, 60, 80, and 100", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Battery Circulator Fan'].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Battery Circulator Fan'] && !model.options.deviceConfig['Battery Circulator Fan'].hide_device);" + }, + "oneOf": [ + { + "title": "BLE", + "enum": [ + "BLE" + ] + }, + { + "title": "BLE/OpenAPI", + "enum": [ + "BLE/OpenAPI" + ] + }, + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "scanDuration": { + "title": "Scan Duration (Default is 1 Second) for BLE", + "type": "number", + "placeholder": 1, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Battery Circulator Fan'] && !model.options.deviceConfig['Battery Circulator Fan'].hide_device && (model.options.deviceConfig['Battery Circulator Fan'].connectionType === 'BLE' || model.options.deviceConfig['Battery Circulator Fan'].connectionType === 'BLE/OpenAPI'));" + } + }, + "webhook": { + "title": "Enable Webhook for all Battery Circulator Fan s", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Battery Circulator Fan'] && !model.options.deviceConfig['Battery Circulator Fan'].hide_device && (model.options.deviceConfig['Battery Circulator Fan'].connectionType === 'OpenAPI' || model.options.deviceConfig['Battery Circulator Fan'].connectionType === 'BLE/OpenAPI'));" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Battery Circulator Fan'] && !model.options.deviceConfig['Battery Circulator Fan'].hide_device);" + } + }, + "maxRetry": { + "title": "Max Retries for BLE", + "type": "number", + "placeholder": "5", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Battery Circulator Fan'] && !model.options.deviceConfig['Battery Circulator Fan'].hide_device && (model.options.deviceConfig['Battery Circulator Fan'].connectionType === 'BLE' || model.options.deviceConfig['Battery Circulator Fan'].connectionType === 'BLE/OpenAPI'));" + } + }, + "maxRetries": { + "title": "Device Max Retries for OpenAPI", + "type": "number", + "placeholder": 5, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Battery Circulator Fan'] && !model.options.deviceConfig['Battery Circulator Fan'].hide_device && (model.options.deviceConfig['Battery Circulator Fan'].connectionType === 'OpenAPI' || model.options.deviceConfig['Battery Circulator Fan'].connectionType === 'BLE/OpenAPI'));" + } + }, + "delayBetweenRetries": { + "title": "Device Delay Between Retries for OpenAPI (In Seconds)", + "type": "number", + "placeholder": 3, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Battery Circulator Fan'] && !model.options.deviceConfig['Battery Circulator Fan'].hide_device && (model.options.deviceConfig['Battery Circulator Fan'].connectionType === 'OpenAPI' || model.options.deviceConfig['Battery Circulator Fan'].connectionType === 'BLE/OpenAPI'));" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && model.options.deviceConfig['Battery Circulator Fan'] && !model.options.deviceConfig['Battery Circulator Fan'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Battery Circulator Fan'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Battery Circulator Fan'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Battery Circulator Fan'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Battery Circulator Fan'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Battery Circulator Fan'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Battery Circulator Fan'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.deviceConfig && !model.options.deviceConfig['Battery Circulator Fan'].hide_device);" + } + } + } + } + } + }, + "irdevices": { + "type": "array", + "items": { + "title": "IR Devices", + "type": "object", + "properties": { + "configDeviceName": { + "title": "Device Name", + "type": "string", + "placeholder": "SwitchBot", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId);" + } + }, + "deviceId": { + "title": "Device ID", + "type": "string", + "placeholder": "81F3UT59513F" + }, + "hide_device": { + "title": "Hide Device", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit.", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId);" + } + }, + "configRemoteType": { + "title": "Remote Type", + "type": "string", + "oneOf": [ + { + "title": "Air Conditioner (IR)", + "enum": [ + "Air Conditioner" + ] + }, + { + "title": "Air Purifier (IR)", + "enum": [ + "Air Purifier" + ] + }, + { + "title": "Camera (IR)", + "enum": [ + "Camera" + ] + }, + { + "title": "DIY Air Conditioner (IR)", + "enum": [ + "DIY Air Conditioner" + ] + }, + { + "title": "DIY Air Purifier (IR)", + "enum": [ + "DIY Air Purifier" + ] + }, + { + "title": "DIY Camera (IR)", + "enum": [ + "DIY Camera" + ] + }, + { + "title": "DIY DVD (IR)", + "enum": [ + "DIY DVD" + ] + }, + { + "title": "DIY Fan (IR)", + "enum": [ + "DIY Fan" + ] + }, + { + "title": "DIY IPTV (IR)", + "enum": [ + "DIY IPTV" + ] + }, + { + "title": "DIY Light (IR)", + "enum": [ + "DIY Light" + ] + }, + { + "title": "DIY Projector (IR)", + "enum": [ + "DIY Projector" + ] + }, + { + "title": "DIY Set Top Box (IR)", + "enum": [ + "DIY Set Top Box" + ] + }, + { + "title": "DIY Speaker (IR)", + "enum": [ + "DIY Speaker" + ] + }, + { + "title": "DIY TV (IR)", + "enum": [ + "DIY TV" + ] + }, + { + "title": "DIY Vacuum Cleaner (IR)", + "enum": [ + "DIY Vacuum Cleaner" + ] + }, + { + "title": "DIY Water Heater (IR)", + "enum": [ + "DIY Water Heater" + ] + }, + { + "title": "DVD (IR)", + "enum": [ + "DVD" + ] + }, + { + "title": "Fan (IR)", + "enum": [ + "Fan" + ] + }, + { + "title": "IPTV (IR)", + "enum": [ + "IPTV" + ] + }, + { + "title": "Light (IR)", + "enum": [ + "Light" + ] + }, + { + "title": "Others (IR)", + "enum": [ + "Others" + ] + }, + { + "title": "Projector (IR)", + "enum": [ + "Projector" + ] + }, + { + "title": "Set Top Box (IR)", + "enum": [ + "Set Top Box" + ] + }, + { + "title": "Speaker (IR)", + "enum": [ + "Speaker" + ] + }, + { + "title": "TV (IR)", + "enum": [ + "TV" + ] + }, + { + "title": "Vacuum Cleaner (IR)", + "enum": [ + "Vacuum Cleaner" + ] + }, + { + "title": "Water Heater (IR)", + "enum": [ + "Water Heater" + ] + } + ], + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device);" + } + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "placeholder": "OpenAPI", + "oneOf": [ + { + "enum": [ + "Disabled" + ], + "title": "Disable" + }, + { + "enum": [ + "OpenAPI" + ], + "title": "OpenAPI" + } + ], + "description": "Enables OpenAPI, if disabled will leave device in HomeKit and commands will not be sent to OpenAPI.", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" + } + }, + "type": { + "title": "What Type of Device do you want to display in the Home App?", + "type": "string", + "required": true, + "default": "", + "oneOf": [ + { + "title": "Door", + "enum": [ + "door" + ] + }, + { + "title": "Fan", + "enum": [ + "fan" + ] + }, + { + "title": "Faucet", + "enum": [ + "faucet" + ] + }, + { + "title": "Garage Door", + "enum": [ + "garagedoor" + ] + }, + { + "title": "Lock", + "enum": [ + "lock" + ] + }, + { + "title": "Outlet", + "enum": [ + "outlet" + ] + }, + { + "title": "Stateful Programmable Switch (Only Works in 3rd Party Home Apps)", + "enum": [ + "stateful" + ] + }, + { + "title": "Switch", + "enum": [ + "switch" + ] + }, + { + "title": "Window", + "enum": [ + "window" + ] + }, + { + "title": "Window Covering", + "enum": [ + "windowcovering" + ] + } + ], + "condition": { + "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType === 'Others' && model.options.irdevices[arrayIndices].deviceId);" + } + }, + "customize": { + "title": "Custom IR Commands", + "type": "boolean", + "description": "Enables Custom IR Commands", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" + } + }, + "customOn": { + "title": "Custom On Command", + "type": "string", + "placeholder": "On", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType && model.options.irdevices[arrayIndices].customize);" + } + }, + "customOff": { + "title": "Custom Off Command", + "type": "string", + "placeholder": "Off", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType && model.options.irdevices[arrayIndices].customize);" + } + }, + "commandType": { + "title": "Command Type", + "type": "string", + "description": "Customize your command type, Default: command", + "placeholder": "tag", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType && model.options.irdevices[arrayIndices].customize);" + } + }, + "disablePushOn": { + "title": "Disable Sending On Command", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" + } + }, + "disablePushOff": { + "title": "Disable Sending Off Command", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" + } + }, + "disablePushDetail": { + "title": "Disable Sending Detail Command(s)", + "description": "Details would be like tempearture for Air Conditioners.", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" + } + }, + "hide_automode": { + "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].configRemoteType === 'Air Conditioner' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Air Conditioner') && model.options.irdevices[arrayIndices].deviceId);" + } + }, + "set_max_heat": { + "title": "IR Air Conditioner Maximum Heat Range", + "type": "number", + "placeholder": "25", + "description": "Set the maximum heat range for the IR Air Conditioner.", + "condition": { + "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);" + } + }, + "set_min_heat": { + "title": "IR Air Conditioner Minimum Heat Range", + "type": "number", + "placeholder": "0", + "description": "Set the minimum heat range for the IR Air Conditioner.", + "condition": { + "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);" + } + }, + "set_max_cool": { + "title": "IR Air Conditioner Maximum Cool Range", + "type": "number", + "placeholder": "35", + "description": "Set the maximum cool range for the IR Air Conditioner.", + "multipleOf": 1, + "condition": { + "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);" + } + }, + "set_min_cool": { + "title": "IR Air Conditioner Minimum Cool Range", + "type": "number", + "placeholder": "10", + "description": "Set the minimum cool range for the IR Air Conditioner.", + "condition": { + "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);" + } + }, + "meterType": { + "title": "Use an existing Switchbot device for temperature/humidity", + "type": "string", + "oneOf": [ + { + "title": "Hub 2", + "enum": [ + "Hub 2" + ] + }, + { + "title": "Meter", + "enum": [ + "Meter" + ] + }, + { + "title": "Meter Plus", + "enum": [ + "MeterPlus" + ] + }, + { + "title": "Meter Plus (JP)", + "enum": [ + "Meter Plus (JP)" + ] + }, + { + "title": "WoIOSensor", + "enum": [ + "WoIOSensor" + ] + }, + { + "title": "Humidifier", + "enum": [ + "Humidifier" + ] + } + ], + "condition": { + "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);" + } + }, + "meterId": { + "title": "Device ID of the device to use for temperature/humidity", + "type": "string", + "condition": { + "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 && model.options.irdevices[arrayIndices].irair?.meterType);" + } + }, + "rotation_speed": { + "title": "Enable Rotation Speed", + "type": "boolean", + "condition": { + "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);" + } + }, + "swing_mode": { + "title": "Enable Swing Mode", + "type": "boolean", + "condition": { + "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": { + "title": "Set Min Step", + "type": "number", + "placeholder": "1", + "condition": { + "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": { + "title": "Set Min", + "type": "number", + "placeholder": "0", + "condition": { + "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": { + "title": "Set Max", + "type": "number", + "placeholder": "100", + "condition": { + "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);" + } + }, + "stateless": { + "title": "Enable Stateless Buttons", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].configRemoteType === 'Light' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Light') && model.options.irdevices[arrayIndices].deviceId);" + } + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" + } + }, + "logging": { + "title": "Device Logging Override Setting", + "type": "string", + "required": true, + "default": "", + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ], + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" + } + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device);" + } + } + }, + "required": [ + "deviceId", + "configRemoteType", + "connectionType", + "logging" + ] + }, + "uniqueItems": true + }, + "irdeviceConfig": { + "type": "object", + "properties": { + "TV": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR TVs", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['TV'] && !model.options.irdeviceConfig['TV'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['TV'] && !model.options.irdeviceConfig['TV'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['TV'] && !model.options.irdeviceConfig['TV'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['TV'] && !model.options.irdeviceConfig['TV'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['TV'] && !model.options.irdeviceConfig['TV'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['TV'] && !model.options.irdeviceConfig['TV'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['TV'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['TV'] && !model.options.irdeviceConfig['TV'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['TV'] && !model.options.irdeviceConfig['TV'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['TV'] && !model.options.irdeviceConfig['TV'].hide_device);" + } + } + } + }, + "DIY TV": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR DIY TVs", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY TV'] && !model.options.irdeviceConfig['DIY TV'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY TV'] && !model.options.irdeviceConfig['DIY TV'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY TV'] && !model.options.irdeviceConfig['DIY TV'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY TV'] && !model.options.irdeviceConfig['DIY TV'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY TV'] && !model.options.irdeviceConfig['DIY TV'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY TV'] && !model.options.irdeviceConfig['DIY TV'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['DIY TV'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY TV'] && !model.options.irdeviceConfig['DIY TV'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY TV'] && !model.options.irdeviceConfig['DIY TV'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY TV'] && !model.options.irdeviceConfig['DIY TV'].hide_device);" + } + } + } + }, + "Projector": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR Projectors", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Projector'] && !model.options.irdeviceConfig['Projector'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Projector'] && !model.options.irdeviceConfig['Projector'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Projector'] && !model.options.irdeviceConfig['Projector'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Projector'] && !model.options.irdeviceConfig['Projector'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Projector'] && !model.options.irdeviceConfig['Projector'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Projector'] && !model.options.irdeviceConfig['Projector'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['Projector'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Projector'] && !model.options.irdeviceConfig['Projector'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Projector'] && !model.options.irdeviceConfig['Projector'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Projector'] && !model.options.irdeviceConfig['Projector'].hide_device);" + } + } + } + }, + "DIY Projector": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR DIY Projectors", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Projector'] && !model.options.irdeviceConfig['DIY Projector'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Projector'] && !model.options.irdeviceConfig['DIY Projector'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Projector'] && !model.options.irdeviceConfig['DIY Projector'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Projector'] && !model.options.irdeviceConfig['DIY Projector'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Projector'] && !model.options.irdeviceConfig['DIY Projector'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Projector'] && !model.options.irdeviceConfig['DIY Projector'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['DIY Projector'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Projector'] && !model.options.irdeviceConfig['DIY Projector'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Projector'] && !model.options.irdeviceConfig['DIY Projector'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Projector'] && !model.options.irdeviceConfig['DIY Projector'].hide_device);" + } + } + } + }, + "Set Top Box": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR Set Top Boxes", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Set Top Box'] && !model.options.irdeviceConfig['Set Top Box'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Set Top Box'] && !model.options.irdeviceConfig['Set Top Box'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Set Top Box'] && !model.options.irdeviceConfig['Set Top Box'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Set Top Box'] && !model.options.irdeviceConfig['Set Top Box'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Set Top Box'] && !model.options.irdeviceConfig['Set Top Box'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Set Top Box'] && !model.options.irdeviceConfig['Set Top Box'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['Set Top Box'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Set Top Box'] && !model.options.irdeviceConfig['Set Top Box'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Set Top Box'] && !model.options.irdeviceConfig['Set Top Box'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Set Top Box'] && !model.options.irdeviceConfig['Set Top Box'].hide_device);" + } + } + } + }, + "DIY Set Top Box": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR DIY Set Top Boxes", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Set Top Box'] && !model.options.irdeviceConfig['DIY Set Top Box'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Set Top Box'] && !model.options.irdeviceConfig['DIY Set Top Box'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Set Top Box'] && !model.options.irdeviceConfig['DIY Set Top Box'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Set Top Box'] && !model.options.irdeviceConfig['DIY Set Top Box'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Set Top Box'] && !model.options.irdeviceConfig['DIY Set Top Box'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Set Top Box'] && !model.options.irdeviceConfig['DIY Set Top Box'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['DIY Set Top Box'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Set Top Box'] && !model.options.irdeviceConfig['DIY Set Top Box'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Set Top Box'] && !model.options.irdeviceConfig['DIY Set Top Box'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Set Top Box'] && !model.options.irdeviceConfig['DIY Set Top Box'].hide_device);" + } + } + } + }, + "IPTV": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR IPTVs", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['IPTV'] && !model.options.irdeviceConfig['IPTV'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['IPTV'] && !model.options.irdeviceConfig['IPTV'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['IPTV'] && !model.options.irdeviceConfig['IPTV'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['IPTV'] && !model.options.irdeviceConfig['IPTV'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['IPTV'] && !model.options.irdeviceConfig['IPTV'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['IPTV'] && !model.options.irdeviceConfig['IPTV'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['IPTV'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['IPTV'] && !model.options.irdeviceConfig['IPTV'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['IPTV'] && !model.options.irdeviceConfig['IPTV'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['IPTV'] && !model.options.irdeviceConfig['IPTV'].hide_device);" + } + } + } + }, + "DIY IPTV": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR DIY IPTVs", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY IPTV'] && !model.options.irdeviceConfig['DIY IPTV'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY IPTV'] && !model.options.irdeviceConfig['DIY IPTV'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY IPTV'] && !model.options.irdeviceConfig['DIY IPTV'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY IPTV'] && !model.options.irdeviceConfig['DIY IPTV'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY IPTV'] && !model.options.irdeviceConfig['DIY IPTV'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY IPTV'] && !model.options.irdeviceConfig['DIY IPTV'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['DIY IPTV'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY IPTV'] && !model.options.irdeviceConfig['DIY IPTV'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY IPTV'] && !model.options.irdeviceConfig['DIY IPTV'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY IPTV'] && !model.options.irdeviceConfig['DIY IPTV'].hide_device);" + } + } + } + }, + "DVD": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR DVDs", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DVD'] && !model.options.irdeviceConfig['DVD'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DVD'] && !model.options.irdeviceConfig['DVD'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DVD'] && !model.options.irdeviceConfig['DVD'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DVD'] && !model.options.irdeviceConfig['DVD'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DVD'] && !model.options.irdeviceConfig['DVD'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DVD'] && !model.options.irdeviceConfig['DVD'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['DVD'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DVD'] && !model.options.irdeviceConfig['DVD'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DVD'] && !model.options.irdeviceConfig['DVD'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DVD'] && !model.options.irdeviceConfig['DVD'].hide_device);" + } + } + } + }, + "DIY DVD": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR DIY DVDs", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY DVD'] && !model.options.irdeviceConfig['DIY DVD'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY DVD'] && !model.options.irdeviceConfig['DIY DVD'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY DVD'] && !model.options.irdeviceConfig['DIY DVD'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY DVD'] && !model.options.irdeviceConfig['DIY DVD'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY DVD'] && !model.options.irdeviceConfig['DIY DVD'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY DVD'] && !model.options.irdeviceConfig['DIY DVD'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['DIY DVD'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY DVD'] && !model.options.irdeviceConfig['DIY DVD'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY DVD'] && !model.options.irdeviceConfig['DIY DVD'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY DVD'] && !model.options.irdeviceConfig['DIY DVD'].hide_device);" + } + } + } + }, + "Speaker": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR Speakers", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Speaker'] && !model.options.irdeviceConfig['Speaker'].hide_device);" }, - { - "title": "DIY Set Top Box (IR)", - "enum": [ - "DIY Set Top Box" - ] + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Speaker'] && !model.options.irdeviceConfig['Speaker'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Speaker'] && !model.options.irdeviceConfig['Speaker'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Speaker'] && !model.options.irdeviceConfig['Speaker'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Speaker'] && !model.options.irdeviceConfig['Speaker'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Speaker'] && !model.options.irdeviceConfig['Speaker'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['Speaker'].hide_device);" }, - { - "title": "DIY Speaker (IR)", - "enum": [ - "DIY Speaker" - ] + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Speaker'] && !model.options.irdeviceConfig['Speaker'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Speaker'] && !model.options.irdeviceConfig['Speaker'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Speaker'] && !model.options.irdeviceConfig['Speaker'].hide_device);" + } + } + } + }, + "DIY Speaker": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR DIY Speakers", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Speaker'] && !model.options.irdeviceConfig['DIY Speaker'].hide_device);" }, - { - "title": "DIY TV (IR)", - "enum": [ - "DIY TV" - ] + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Speaker'] && !model.options.irdeviceConfig['DIY Speaker'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Speaker'] && !model.options.irdeviceConfig['DIY Speaker'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Speaker'] && !model.options.irdeviceConfig['DIY Speaker'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Speaker'] && !model.options.irdeviceConfig['DIY Speaker'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Speaker'] && !model.options.irdeviceConfig['DIY Speaker'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['DIY Speaker'].hide_device);" }, - { - "title": "DIY Vacuum Cleaner (IR)", - "enum": [ - "DIY Vacuum Cleaner" - ] + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Speaker'] && !model.options.irdeviceConfig['DIY Speaker'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Speaker'] && !model.options.irdeviceConfig['DIY Speaker'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Speaker'] && !model.options.irdeviceConfig['DIY Speaker'].hide_device);" + } + } + } + }, + "Fan": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR Fans", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Fan'] && !model.options.irdeviceConfig['Fan'].hide_device);" }, - { - "title": "DIY Water Heater (IR)", - "enum": [ - "DIY Water Heater" - ] + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Fan'] && !model.options.irdeviceConfig['Fan'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Fan'] && !model.options.irdeviceConfig['Fan'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Fan'] && !model.options.irdeviceConfig['Fan'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Fan'] && !model.options.irdeviceConfig['Fan'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Fan'] && !model.options.irdeviceConfig['Fan'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['Fan'].hide_device);" }, - { - "title": "DVD (IR)", - "enum": [ - "DVD" - ] + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Fan'] && !model.options.irdeviceConfig['Fan'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Fan'] && !model.options.irdeviceConfig['Fan'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Fan'] && !model.options.irdeviceConfig['Fan'].hide_device);" + } + } + } + }, + "DIY Fan": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR DIY Fans", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Fan'] && !model.options.irdeviceConfig['DIY Fan'].hide_device);" }, - { - "title": "Fan (IR)", - "enum": [ - "Fan" - ] + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Fan'] && !model.options.irdeviceConfig['DIY Fan'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Fan'] && !model.options.irdeviceConfig['DIY Fan'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Fan'] && !model.options.irdeviceConfig['DIY Fan'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Fan'] && !model.options.irdeviceConfig['DIY Fan'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Fan'] && !model.options.irdeviceConfig['DIY Fan'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['DIY Fan'].hide_device);" }, - { - "title": "IPTV (IR)", - "enum": [ - "IPTV" - ] + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Fan'] && !model.options.irdeviceConfig['DIY Fan'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Fan'] && !model.options.irdeviceConfig['DIY Fan'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Fan'] && !model.options.irdeviceConfig['DIY Fan'].hide_device);" + } + } + } + }, + "Air Conditioner": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR Air Conditioners", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Conditioner'] && !model.options.irdeviceConfig['Air Conditioner'].hide_device);" }, - { - "title": "Light (IR)", - "enum": [ - "Light" - ] + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Conditioner'] && !model.options.irdeviceConfig['Air Conditioner'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Conditioner'] && !model.options.irdeviceConfig['Air Conditioner'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Conditioner'] && !model.options.irdeviceConfig['Air Conditioner'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Conditioner'] && !model.options.irdeviceConfig['Air Conditioner'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Conditioner'] && !model.options.irdeviceConfig['Air Conditioner'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['Air Conditioner'].hide_device);" }, - { - "title": "Others (IR)", - "enum": [ - "Others" - ] + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Conditioner'] && !model.options.irdeviceConfig['Air Conditioner'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Conditioner'] && !model.options.irdeviceConfig['Air Conditioner'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Conditioner'] && !model.options.irdeviceConfig['Air Conditioner'].hide_device);" + } + } + } + }, + "DIY Air Conditioner": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR DIY Air Conditioners", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Conditioner'] && !model.options.irdeviceConfig['DIY Air Conditioner'].hide_device);" }, - { - "title": "Projector (IR)", - "enum": [ - "Projector" - ] + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Conditioner'] && !model.options.irdeviceConfig['DIY Air Conditioner'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Conditioner'] && !model.options.irdeviceConfig['DIY Air Conditioner'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Conditioner'] && !model.options.irdeviceConfig['DIY Air Conditioner'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Conditioner'] && !model.options.irdeviceConfig['DIY Air Conditioner'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Conditioner'] && !model.options.irdeviceConfig['DIY Air Conditioner'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['DIY Air Conditioner'].hide_device);" }, - { - "title": "Set Top Box (IR)", - "enum": [ - "Set Top Box" - ] + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Conditioner'] && !model.options.irdeviceConfig['DIY Air Conditioner'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Conditioner'] && !model.options.irdeviceConfig['DIY Air Conditioner'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Conditioner'] && !model.options.irdeviceConfig['DIY Air Conditioner'].hide_device);" + } + } + } + }, + "Light": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR Lights", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Light'] && !model.options.irdeviceConfig['Light'].hide_device);" }, - { - "title": "Speaker (IR)", - "enum": [ - "Speaker" - ] + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Light'] && !model.options.irdeviceConfig['Light'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Light'] && !model.options.irdeviceConfig['Light'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Light'] && !model.options.irdeviceConfig['Light'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Light'] && !model.options.irdeviceConfig['Light'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Light'] && !model.options.irdeviceConfig['Light'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['Light'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Light'] && !model.options.irdeviceConfig['Light'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Light'] && !model.options.irdeviceConfig['Light'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Light'] && !model.options.irdeviceConfig['Light'].hide_device);" + } + } + } + }, + "DIY Light": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR DIY Lights", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Light'] && !model.options.irdeviceConfig['DIY Light'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Light'] && !model.options.irdeviceConfig['DIY Light'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Light'] && !model.options.irdeviceConfig['DIY Light'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Light'] && !model.options.irdeviceConfig['DIY Light'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Light'] && !model.options.irdeviceConfig['DIY Light'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Light'] && !model.options.irdeviceConfig['DIY Light'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['DIY Light'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Light'] && !model.options.irdeviceConfig['DIY Light'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Light'] && !model.options.irdeviceConfig['DIY Light'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Light'] && !model.options.irdeviceConfig['DIY Light'].hide_device);" + } + } + } + }, + "Air Purifier": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR Air Purifiers", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Purifier'] && !model.options.irdeviceConfig['Air Purifier'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Purifier'] && !model.options.irdeviceConfig['Air Purifier'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Purifier'] && !model.options.irdeviceConfig['Air Purifier'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Purifier'] && !model.options.irdeviceConfig['Air Purifier'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Purifier'] && !model.options.irdeviceConfig['Air Purifier'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Purifier'] && !model.options.irdeviceConfig['Air Purifier'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['Air Purifier'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Purifier'] && !model.options.irdeviceConfig['Air Purifier'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Purifier'] && !model.options.irdeviceConfig['Air Purifier'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Air Purifier'] && !model.options.irdeviceConfig['Air Purifier'].hide_device);" + } + } + } + }, + "DIY Air Purifier": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR DIY Air Purifiers", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Purifier'] && !model.options.irdeviceConfig['DIY Air Purifier'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Purifier'] && !model.options.irdeviceConfig['DIY Air Purifier'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Purifier'] && !model.options.irdeviceConfig['DIY Air Purifier'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Purifier'] && !model.options.irdeviceConfig['DIY Air Purifier'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Purifier'] && !model.options.irdeviceConfig['DIY Air Purifier'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Purifier'] && !model.options.irdeviceConfig['DIY Air Purifier'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['DIY Air Purifier'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Purifier'] && !model.options.irdeviceConfig['DIY Air Purifier'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Purifier'] && !model.options.irdeviceConfig['DIY Air Purifier'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Air Purifier'] && !model.options.irdeviceConfig['DIY Air Purifier'].hide_device);" + } + } + } + }, + "Water Heater": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR Water Heaters", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Water Heater'] && !model.options.irdeviceConfig['Water Heater'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Water Heater'] && !model.options.irdeviceConfig['Water Heater'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Water Heater'] && !model.options.irdeviceConfig['Water Heater'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Water Heater'] && !model.options.irdeviceConfig['Water Heater'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Water Heater'] && !model.options.irdeviceConfig['Water Heater'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Water Heater'] && !model.options.irdeviceConfig['Water Heater'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['Water Heater'].hide_device);" }, - { - "title": "TV (IR)", - "enum": [ - "TV" - ] + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Water Heater'] && !model.options.irdeviceConfig['Water Heater'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Water Heater'] && !model.options.irdeviceConfig['Water Heater'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Water Heater'] && !model.options.irdeviceConfig['Water Heater'].hide_device);" + } + } + } + }, + "DIY Water Heater": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR DIY Water Heaters", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Water Heater'] && !model.options.irdeviceConfig['DIY Water Heater'].hide_device);" }, - { - "title": "Vacuum Cleaner (IR)", - "enum": [ - "Vacuum Cleaner" - ] + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Water Heater'] && !model.options.irdeviceConfig['DIY Water Heater'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Water Heater'] && !model.options.irdeviceConfig['DIY Water Heater'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Water Heater'] && !model.options.irdeviceConfig['DIY Water Heater'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Water Heater'] && !model.options.irdeviceConfig['DIY Water Heater'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Water Heater'] && !model.options.irdeviceConfig['DIY Water Heater'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['DIY Water Heater'].hide_device);" }, - { - "title": "Water Heater (IR)", - "enum": [ - "Water Heater" - ] + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Water Heater'] && !model.options.irdeviceConfig['DIY Water Heater'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Water Heater'] && !model.options.irdeviceConfig['DIY Water Heater'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Water Heater'] && !model.options.irdeviceConfig['DIY Water Heater'].hide_device);" } - ], - "condition": { - "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device);" } - }, - "connectionType": { - "title": "Connection Type", - "type": "string", - "placeholder": "OpenAPI", - "oneOf": [ - { - "enum": [ - "Disabled" - ], - "title": "Disable" + } + }, + "Vacuum Cleaner": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR Vacuum Cleaners", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Vacuum Cleaner'] && !model.options.irdeviceConfig['Vacuum Cleaner'].hide_device);" }, - { - "enum": [ - "OpenAPI" - ], - "title": "OpenAPI" + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Vacuum Cleaner'] && !model.options.irdeviceConfig['Vacuum Cleaner'].hide_device);" } - ], - "description": "Enables OpenAPI, if disabled will leave device in HomeKit and commands will not be sent to OpenAPI.", - "condition": { - "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" - } - }, - "customize": { - "title": "Custom IR Commands", - "type": "boolean", - "description": "Enables Custom IR Commands", - "condition": { - "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" - } - }, - "customOn": { - "title": "Custom On Command", - "type": "string", - "placeholder": "On", - "condition": { - "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType && model.options.irdevices[arrayIndices].customize);" - } - }, - "customOff": { - "title": "Custom Off Command", - "type": "string", - "placeholder": "Off", - "condition": { - "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType && model.options.irdevices[arrayIndices].customize);" - } - }, - "commandType": { - "title": "Command Type", - "type": "string", - "description": "Customize your command type, Default: command", - "placeholder": "tag", - "condition": { - "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType && model.options.irdevices[arrayIndices].customize);" - } - }, - "disablePushOn": { - "title": "Disable Sending On Command", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" - } - }, - "disablePushOff": { - "title": "Disable Sending Off Command", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" - } - }, - "disablePushDetail": { - "title": "Disable Sending Detail Command(s)", - "description": "Details would be like tempearture for Air Conditioners.", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" - } - }, - "irair": { - "type": "object", - "properties": { - "hide_automode": { - "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].configRemoteType === 'Air Conditioner' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Air Conditioner') && model.options.irdevices[arrayIndices].deviceId);" - } - }, - "set_max_heat": { - "title": "IR Air Conditioner Maximum Heat Range", - "type": "number", - "placeholder": "25", - "description": "Set the maximum heat range for the IR Air Conditioner.", - "condition": { - "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);" - } - }, - "set_min_heat": { - "title": "IR Air Conditioner Minimum Heat Range", - "type": "number", - "placeholder": "0", - "description": "Set the minimum heat range for the IR Air Conditioner.", - "condition": { - "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);" - } - }, - "set_max_cool": { - "title": "IR Air Conditioner Maximum Cool Range", - "type": "number", - "placeholder": "35", - "description": "Set the maximum cool range for the IR Air Conditioner.", - "multipleOf": 1, - "condition": { - "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);" - } - }, - "set_min_cool": { - "title": "IR Air Conditioner Minimum Cool Range", - "type": "number", - "placeholder": "10", - "description": "Set the minimum cool range for the IR Air Conditioner.", - "condition": { - "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);" - } - }, - "meterType": { - "title": "Use an existing Switchbot device for temperature/humidity", - "type": "string", - "oneOf": [ - { - "title": "Hub 2", - "enum": [ - "Hub 2" - ] - }, - { - "title": "Meter", - "enum": [ - "Meter" - ] - }, - { - "title": "Meter Plus", - "enum": [ - "MeterPlus" - ] - }, - { - "title": "Meter Plus (JP)", - "enum": [ - "Meter Plus (JP)" - ] - }, - { - "title": "WoIOSensor", - "enum": [ - "WoIOSensor" - ] - }, - { - "title": "Humidifier", - "enum": [ - "Humidifier" - ] - } - ], - "condition": { - "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);" - } - }, - "meterId": { - "title": "Device ID of the device to use for temperature/humidity", - "type": "string", - "condition": { - "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 && model.options.irdevices[arrayIndices].irair?.meterType);" - } - } - } - }, - "irfan": { - "type": "object", - "properties": { - "swing_mode": { - "title": "Enable Swing Mode", - "type": "boolean", - "condition": { - "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", - "type": "boolean", - "condition": { - "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": { - "title": "Set Min Step", - "type": "number", - "placeholder": "1", - "condition": { - "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": { - "title": "Set Min", - "type": "number", - "placeholder": "0", - "condition": { - "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": { - "title": "Set Max", - "type": "number", - "placeholder": "100", - "condition": { - "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);" - } - } - } - }, - "irlight": { - "type": "object", - "properties": { - "stateless": { - "title": "Enable Stateless Buttons", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && (model.options.irdevices[arrayIndices].configRemoteType === 'Light' || model.options.irdevices[arrayIndices].configRemoteType === 'DIY Light') && model.options.irdevices[arrayIndices].deviceId);" - } - } - } - }, - "other": { - "type": "object", - "properties": { - "deviceType": { - "title": "What Type of Device do you want to display in the Home App?", - "type": "string", - "required": true, - "default": "", - "oneOf": [ - { - "title": "Door", - "enum": [ - "door" - ] - }, - { - "title": "Fan", - "enum": [ - "fan" - ] - }, - { - "title": "Faucet", - "enum": [ - "faucet" - ] - }, - { - "title": "Garage Door", - "enum": [ - "garagedoor" - ] - }, - { - "title": "Lock", - "enum": [ - "lock" - ] - }, - { - "title": "Outlet", - "enum": [ - "outlet" - ] - }, - { - "title": "Stateful Programmable Switch (Only Works in 3rd Party Home Apps)", - "enum": [ - "stateful" - ] - }, - { - "title": "Switch", - "enum": [ - "switch" - ] - }, - { - "title": "Window", - "enum": [ - "window" - ] - }, - { - "title": "Window Covering", - "enum": [ - "windowcovering" - ] - } - ], - "condition": { - "functionBody": "return (model.options && model.options.irdevices && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType === 'Others' && model.options.irdevices[arrayIndices].deviceId);" + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Vacuum Cleaner'] && !model.options.irdeviceConfig['Vacuum Cleaner'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Vacuum Cleaner'] && !model.options.irdeviceConfig['Vacuum Cleaner'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Vacuum Cleaner'] && !model.options.irdeviceConfig['Vacuum Cleaner'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Vacuum Cleaner'] && !model.options.irdeviceConfig['Vacuum Cleaner'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['Vacuum Cleaner'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Vacuum Cleaner'] && !model.options.irdeviceConfig['Vacuum Cleaner'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Vacuum Cleaner'] && !model.options.irdeviceConfig['Vacuum Cleaner'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Vacuum Cleaner'] && !model.options.irdeviceConfig['Vacuum Cleaner'].hide_device);" } } - }, - "firmware": { - "title": "Firmware Override", - "type": "string", - "placeholder": "1.2.8", - "condition": { - "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" - } - }, - "external": { - "title": "External Accessory", - "type": "boolean", - "condition": { - "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" + } + }, + "DIY Vacuum Cleaner": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR DIY Vacuum Cleaners", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Vacuum Cleaner'] && !model.options.irdeviceConfig['DIY Vacuum Cleaner'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Vacuum Cleaner'] && !model.options.irdeviceConfig['DIY Vacuum Cleaner'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Vacuum Cleaner'] && !model.options.irdeviceConfig['DIY Vacuum Cleaner'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Vacuum Cleaner'] && !model.options.irdeviceConfig['DIY Vacuum Cleaner'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Vacuum Cleaner'] && !model.options.irdeviceConfig['DIY Vacuum Cleaner'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Vacuum Cleaner'] && !model.options.irdeviceConfig['DIY Vacuum Cleaner'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['DIY Vacuum Cleaner'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Vacuum Cleaner'] && !model.options.irdeviceConfig['DIY Vacuum Cleaner'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Vacuum Cleaner'] && !model.options.irdeviceConfig['DIY Vacuum Cleaner'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Vacuum Cleaner'] && !model.options.irdeviceConfig['DIY Vacuum Cleaner'].hide_device);" + } } - }, - "logging": { - "title": "Device Logging Override Setting", - "type": "string", - "required": true, - "default": "", - "oneOf": [ - { - "title": "Debug Logging", - "enum": [ - "debug" - ] + } + }, + "Camera": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR Cameras", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Camera'] && !model.options.irdeviceConfig['Camera'].hide_device);" }, - { - "title": "Default Logging", - "enum": [ - "" - ] + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Camera'] && !model.options.irdeviceConfig['Camera'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Camera'] && !model.options.irdeviceConfig['Camera'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Camera'] && !model.options.irdeviceConfig['Camera'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Camera'] && !model.options.irdeviceConfig['Camera'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Camera'] && !model.options.irdeviceConfig['Camera'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['Camera'].hide_device);" }, - { - "title": "No Logging", - "enum": [ - "none" - ] + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Camera'] && !model.options.irdeviceConfig['Camera'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Camera'] && !model.options.irdeviceConfig['Camera'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Camera'] && !model.options.irdeviceConfig['Camera'].hide_device);" + } + } + } + }, + "DIY Camera": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All IR DIY Cameras", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Camera'] && !model.options.irdeviceConfig['DIY Camera'].hide_device);" }, - { - "title": "Standard Logging", - "enum": [ - "standard" - ] + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Camera'] && !model.options.irdeviceConfig['DIY Camera'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Camera'] && !model.options.irdeviceConfig['DIY Camera'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Camera'] && !model.options.irdeviceConfig['DIY Camera'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Camera'] && !model.options.irdeviceConfig['DIY Camera'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Camera'] && !model.options.irdeviceConfig['DIY Camera'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['DIY Camera'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Camera'] && !model.options.irdeviceConfig['DIY Camera'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Camera'] && !model.options.irdeviceConfig['DIY Camera'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['DIY Camera'] && !model.options.irdeviceConfig['DIY Camera'].hide_device);" } - ], - "condition": { - "functionBody": "return (model.options && model.options.irdevices && model.options.irdevices[arrayIndices].deviceId && !model.options.irdevices[arrayIndices].hide_device && model.options.irdevices[arrayIndices].configRemoteType);" } } }, - "required": [ - "deviceId", - "configRemoteType", - "connectionType", - "logging" - ] - }, - "uniqueItems": true + "Others": { + "type": "object", + "properties": { + "hide_device": { + "title": "Hide All Others", + "type": "boolean", + "description": "If true, device will be removed or hidden from HomeKit." + }, + "connectionType": { + "title": "Connection Type", + "type": "string", + "description": "Bluetooth (BLE) API is only available on certain Device Types, see the Wiki for more information.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Others'] && !model.options.irdeviceConfig['Others'].hide_device);" + }, + "oneOf": [ + { + "title": "Disable", + "enum": [ + "Disabled" + ] + }, + { + "title": "OpenAPI", + "enum": [ + "OpenAPI" + ] + } + ] + }, + "external": { + "title": "External Accessory", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Others'] && !model.options.irdeviceConfig['Others'].hide_device);" + } + }, + "disableCaching": { + "title": "Disable Caching", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Others'] && !model.options.irdeviceConfig['Others'].hide_device);" + } + }, + "offline": { + "title": "Offline as Off", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Others'] && !model.options.irdeviceConfig['Others'].hide_device);" + } + }, + "history": { + "title": "EVE History", + "type": "boolean", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Others'] && !model.options.irdeviceConfig['Others'].hide_device);" + } + }, + "firmware": { + "title": "Firmware Override", + "type": "string", + "placeholder": "1.2.8", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Others'] && !model.options.irdeviceConfig['Others'].hide_device);" + } + }, + "logging": { + "title": "Logging by Device Type", + "type": "string", + "required": true, + "default": "", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && !model.options.irdeviceConfig['Others'].hide_device);" + }, + "oneOf": [ + { + "title": "Debug Logging", + "enum": [ + "debug" + ] + }, + { + "title": "Default Logging", + "enum": [ + "" + ] + }, + { + "title": "No Logging", + "enum": [ + "none" + ] + }, + { + "title": "Standard Logging", + "enum": [ + "standard" + ] + } + ] + }, + "refreshRate": { + "title": "Device Refresh Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Others'] && !model.options.irdeviceConfig['Others'].hide_device);" + } + }, + "updateRate": { + "title": "Update Rate", + "type": "number", + "minimum": 1, + "placeholder": 5, + "description": "Indicates the number of seconds before refreshing status while updating progress.", + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Others'] && !model.options.irdeviceConfig['Others'].hide_device);" + } + }, + "pushRate": { + "title": "Device Push Rate", + "type": "number", + "placeholder": 360, + "condition": { + "functionBody": "return (model.options && model.options.irdeviceConfig && model.options.irdeviceConfig['Others'] && !model.options.irdeviceConfig['Others'].hide_device);" + } + } + } + } + } }, "BLE": { "title": "Enable BLE Scanning", "type": "boolean" }, + "discoverBLE": { + "title": "Discover BLE Devices", + "type": "boolean" + }, + "disableLogsforBLE": { + "title": "Disable Logging from BLE", + "type": "boolean" + }, + "disableLogsforOpenAPI": { + "title": "Disable Logging from OpenAPI", + "type": "boolean" + }, "webhookURL": { "title": "Webhook URL", "type": "string", @@ -1814,123 +11865,977 @@ "description": "If true, invalid characters will be allowed in the device name." } }, - "required": [ - "name", - "logging" - ] - } - } - }, - "layout": [ - { - "type": "fieldset", - "title": "SwitchBot Account Info", - "expandable": true, - "expanded": false, - "items": [ - "credentials.token", - "credentials.secret" - ] - }, - { - "type": "fieldset", - "title": "SwitchBot Device Settings", - "expandable": true, - "expanded": false, - "items": [ + "required": [ + "name", + "logging" + ] + } + } + }, + "layout": [ + { + "type": "fieldset", + "title": "SwitchBot Account Info", + "expandable": true, + "expanded": false, + "items": [ + "credentials.token", + "credentials.secret" + ] + }, + { + "type": "fieldset", + "title": "SwitchBot Device Settings", + "expandable": true, + "expanded": false, + "items": [ + { + "type": "help", + "helpvalue": "With SwitchBot Device Setting, you can set device specific settings based on deviceId. This will override Device Type Settings" + }, + { + "key": "options.devices", + "notitle": false, + "type": "tabarray", + "title": "{{ value.configDeviceName || value.deviceId || 'New SwitchBot Device' }}", + "expandable": true, + "expanded": false, + "draggable": true, + "orderable": true, + "items": [ + "options.devices[].configDeviceName", + "options.devices[].deviceId", + "options.devices[].hide_device", + "options.devices[].configDeviceType", + "options.devices[].connectionType", + "options.devices[].scanDuration", + "options.devices[].customBLEaddress", + "options.devices[].webhook", + "options.devices[].type", + "options.devices[].mode", + "options.devices[].mapping", + "options.devices[].allowPush", + "options.devices[].doublePress", + "options.devices[].pushRatePress", + "options.devices[].hide_temperature", + "options.devices[].convertUnitTo", + "options.devices[].hide_humidity", + "options.devices[].hide_lightsensor", + "options.devices[].hide_motionsensor", + "options.devices[].hide_contactsensor", + "options.devices[].hide_leak", + "options.devices[].disable_group", + "options.devices[].disableCaching", + "options.devices[].adaptiveLightingShift", + "options.devices[].activate_latchbutton", + "options.devices[].dry", + "options.devices[].set_minStep", + "options.devices[].set_min", + "options.devices[].set_max", + "options.devices[].set_minlux", + "options.devices[].set_maxlux", + "options.devices[].setOpenMode", + "options.devices[].setCloseMode", + "options.devices[].silentModeSwitch", + "options.devices[].maxRetry", + "options.devices[].maxRetries", + "options.devices[].delayBetweenRetries", + "options.devices[].mqttURL", + "options.devices[].mqttOptions", + "options.devices[].mqttPubOptions", + "options.devices[].history", + "options.devices[].offline", + "options.devices[].firmware", + "options.devices[].external", + "options.devices[].logging", + { + "key": "options.devices[].refreshRate", + "description": "Specifies the interval, in seconds, for retrieving the latest device status from the SwitchBot API. This interval applies only to this specific device." + }, + { + "key": "options.devices[].updateRate", + "description": "Specifies the interval, in seconds, at which this device will request updates from the SwitchBot API while the device is in motion, for Curtain(s) and Blind Tilt(s) only." + }, + { + "key": "options.devices[].pushRate", + "description": "Specifies the interval, in seconds, between pushes to the SwitchBot API for this specific device." + } + ] + } + ] + }, + { + "type": "fieldset", + "title": "SwitchBot Device Type Settings", + "expandable": true, + "expanded": false, + "items": [ + { + "type": "help", + "helpvalue": "Setting a Device Type Setting will not override indiviudally set setting on SwithBot Device Settings." + }, + { + "type": "fieldset", + "title": "Humidifier Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Humidifier'].hide_device", + "options.deviceConfig['Humidifier'].hide_temperature", + "options.deviceConfig['Humidifier'].convertUnitTo", + "options.deviceConfig['Humidifier'].set_minStep", + "options.deviceConfig['Humidifier'].connectionType", + "options.deviceConfig['Humidifier'].scanDuration", + "options.deviceConfig['Humidifier'].webhook", + "options.deviceConfig['Humidifier'].external", + "options.deviceConfig['Humidifier'].maxRetry", + "options.deviceConfig['Humidifier'].maxRetries", + "options.deviceConfig['Humidifier'].delayBetweenRetries", + "options.deviceConfig['Humidifier'].disableCaching", + "options.deviceConfig['Humidifier'].offline", + "options.deviceConfig['Humidifier'].history", + "options.deviceConfig['Humidifier'].firmware", + "options.deviceConfig['Humidifier'].logging", + "options.deviceConfig['Humidifier'].refreshRate", + "options.deviceConfig['Humidifier'].updateRate", + "options.deviceConfig['Humidifier'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Hub 2 Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Hub 2'].hide_device", + "options.deviceConfig['Hub 2'].hide_temperature", + "options.deviceConfig['Hub 2'].convertUnitTo", + "options.deviceConfig['Hub 2'].hide_humidity", + "options.deviceConfig['Hub 2'].connectionType", + "options.deviceConfig['Hub 2'].scanDuration", + "options.deviceConfig['Hub 2'].webhook", + "options.deviceConfig['Hub 2'].external", + "options.deviceConfig['Hub 2'].maxRetry", + "options.deviceConfig['Hub 2'].maxRetries", + "options.deviceConfig['Hub 2'].delayBetweenRetries", + "options.deviceConfig['Hub 2'].disableCaching", + "options.deviceConfig['Hub 2'].offline", + "options.deviceConfig['Hub 2'].history", + "options.deviceConfig['Hub 2'].firmware", + "options.deviceConfig['Hub 2'].logging", + "options.deviceConfig['Hub 2'].refreshRate", + "options.deviceConfig['Hub 2'].updateRate", + "options.deviceConfig['Hub 2'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Bot Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Bot'].hide_device", + "options.deviceConfig['Bot'].mode", + "options.deviceConfig['Bot'].type", + "options.deviceConfig['Bot'].doublePress", + "options.deviceConfig['Bot'].pushRatePress", + "options.deviceConfig['Bot'].allowPush", + "options.deviceConfig['Bot'].connectionType", + "options.deviceConfig['Bot'].scanDuration", + "options.deviceConfig['Bot'].webhook", + "options.deviceConfig['Bot'].external", + "options.deviceConfig['Bot'].maxRetry", + "options.deviceConfig['Bot'].maxRetries", + "options.deviceConfig['Bot'].delayBetweenRetries", + "options.deviceConfig['Bot'].disableCaching", + "options.deviceConfig['Bot'].offline", + "options.deviceConfig['Bot'].history", + "options.deviceConfig['Bot'].firmware", + "options.deviceConfig['Bot'].logging", + "options.deviceConfig['Bot'].refreshRate", + "options.deviceConfig['Bot'].updateRate", + "options.deviceConfig['Bot'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Meter Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Meter'].hide_device", + "options.deviceConfig['Meter'].hide_temperature", + "options.deviceConfig['Meter'].convertUnitTo", + "options.deviceConfig['Meter'].hide_humidity", + "options.deviceConfig['Meter'].connectionType", + "options.deviceConfig['Meter'].scanDuration", + "options.deviceConfig['Meter'].webhook", + "options.deviceConfig['Meter'].external", + "options.deviceConfig['Meter'].maxRetry", + "options.deviceConfig['Meter'].maxRetries", + "options.deviceConfig['Meter'].delayBetweenRetries", + "options.deviceConfig['Meter'].disableCaching", + "options.deviceConfig['Meter'].offline", + "options.deviceConfig['Meter'].history", + "options.deviceConfig['Meter'].firmware", + "options.deviceConfig['Meter'].logging", + "options.deviceConfig['Meter'].refreshRate", + "options.deviceConfig['Meter'].updateRate", + "options.deviceConfig['Meter'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Meter Plus Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['MeterPlus'].hide_device", + "options.deviceConfig['MeterPlus'].hide_temperature", + "options.deviceConfig['MeterPlus'].convertUnitTo", + "options.deviceConfig['MeterPlus'].hide_humidity", + "options.deviceConfig['MeterPlus'].connectionType", + "options.deviceConfig['MeterPlus'].scanDuration", + "options.deviceConfig['MeterPlus'].webhook", + "options.deviceConfig['MeterPlus'].external", + "options.deviceConfig['MeterPlus'].maxRetry", + "options.deviceConfig['MeterPlus'].maxRetries", + "options.deviceConfig['MeterPlus'].delayBetweenRetries", + "options.deviceConfig['MeterPlus'].disableCaching", + "options.deviceConfig['MeterPlus'].offline", + "options.deviceConfig['MeterPlus'].history", + "options.deviceConfig['MeterPlus'].firmware", + "options.deviceConfig['MeterPlus'].logging", + "options.deviceConfig['MeterPlus'].refreshRate", + "options.deviceConfig['MeterPlus'].updateRate", + "options.deviceConfig['MeterPlus'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Meter Plus (JP) Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Meter Plus (JP)'].hide_device", + "options.deviceConfig['Meter Plus (JP)'].hide_temperature", + "options.deviceConfig['Meter Plus (JP)'].convertUnitTo", + "options.deviceConfig['Meter Plus (JP)'].hide_humidity", + "options.deviceConfig['Meter Plus (JP)'].connectionType", + "options.deviceConfig['Meter Plus (JP)'].scanDuration", + "options.deviceConfig['Meter Plus (JP)'].webhook", + "options.deviceConfig['Meter Plus (JP)'].external", + "options.deviceConfig['Meter Plus (JP)'].maxRetry", + "options.deviceConfig['Meter Plus (JP)'].maxRetries", + "options.deviceConfig['Meter Plus (JP)'].delayBetweenRetries", + "options.deviceConfig['Meter Plus (JP)'].disableCaching", + "options.deviceConfig['Meter Plus (JP)'].offline", + "options.deviceConfig['Meter Plus (JP)'].history", + "options.deviceConfig['Meter Plus (JP)'].firmware", + "options.deviceConfig['Meter Plus (JP)'].logging", + "options.deviceConfig['Meter Plus (JP)'].refreshRate", + "options.deviceConfig['Meter Plus (JP)'].updateRate", + "options.deviceConfig['Meter Plus (JP)'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Indoor Outdoor Sensor Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['WoIOSensor'].hide_device", + "options.deviceConfig['WoIOSensor'].hide_temperature", + "options.deviceConfig['WoIOSensor'].convertUnitTo", + "options.deviceConfig['WoIOSensor'].hide_humidity", + "options.deviceConfig['WoIOSensor'].connectionType", + "options.deviceConfig['WoIOSensor'].scanDuration", + "options.deviceConfig['WoIOSensor'].webhook", + "options.deviceConfig['WoIOSensor'].external", + "options.deviceConfig['WoIOSensor'].maxRetry", + "options.deviceConfig['WoIOSensor'].maxRetries", + "options.deviceConfig['WoIOSensor'].delayBetweenRetries", + "options.deviceConfig['WoIOSensor'].disableCaching", + "options.deviceConfig['WoIOSensor'].offline", + "options.deviceConfig['WoIOSensor'].history", + "options.deviceConfig['WoIOSensor'].firmware", + "options.deviceConfig['WoIOSensor'].logging", + "options.deviceConfig['WoIOSensor'].refreshRate", + "options.deviceConfig['WoIOSensor'].updateRate", + "options.deviceConfig['WoIOSensor'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Water Detector Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Water Detector']hide_device", + "options.deviceConfig['Water Detector']hide_leak", + "options.deviceConfig['Water Detector']dry", + "options.deviceConfig['Water Detector'].connectionType", + "options.deviceConfig['Water Detector'].scanDuration", + "options.deviceConfig['Water Detector'].webhook", + "options.deviceConfig['Water Detector'].external", + "options.deviceConfig['Water Detector'].maxRetry", + "options.deviceConfig['Water Detector'].maxRetries", + "options.deviceConfig['Water Detector'].delayBetweenRetries", + "options.deviceConfig['Water Detector'].disableCaching", + "options.deviceConfig['Water Detector'].offline", + "options.deviceConfig['Water Detector'].history", + "options.deviceConfig['Water Detector'].firmware", + "options.deviceConfig['Water Detector'].logging", + "options.deviceConfig['Water Detector'].refreshRate", + "options.deviceConfig['Water Detector'].updateRate", + "options.deviceConfig['Water Detector'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Motion Sensor Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Motion Sensor'].hide_device", + "options.deviceConfig['Motion Sensor'].hide_lightsensor", + "options.deviceConfig['Motion Sensor'].set_minlux", + "options.deviceConfig['Motion Sensor'].set_maxlux", + "options.deviceConfig['Motion Sensor'].connectionType", + "options.deviceConfig['Motion Sensor'].scanDuration", + "options.deviceConfig['Motion Sensor'].webhook", + "options.deviceConfig['Motion Sensor'].external", + "options.deviceConfig['Motion Sensor'].maxRetry", + "options.deviceConfig['Motion Sensor'].maxRetries", + "options.deviceConfig['Motion Sensor'].delayBetweenRetries", + "options.deviceConfig['Motion Sensor'].disableCaching", + "options.deviceConfig['Motion Sensor'].offline", + "options.deviceConfig['Motion Sensor'].history", + "options.deviceConfig['Motion Sensor'].firmware", + "options.deviceConfig['Motion Sensor'].logging", + "options.deviceConfig['Motion Sensor'].refreshRate", + "options.deviceConfig['Motion Sensor'].updateRate", + "options.deviceConfig['Motion Sensor'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Contact Sensor Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Contact Sensor'].hide_device", + "options.deviceConfig['Contact Sensor'].hide_lightsensor", + "options.deviceConfig['Contact Sensor'].set_minlux", + "options.deviceConfig['Contact Sensor'].set_maxlux", + "options.deviceConfig['Contact Sensor'].hide_motionsensor", + "options.deviceConfig['Contact Sensor'].connectionType", + "options.deviceConfig['Contact Sensor'].scanDuration", + "options.deviceConfig['Contact Sensor'].webhook", + "options.deviceConfig['Contact Sensor'].external", + "options.deviceConfig['Contact Sensor'].maxRetry", + "options.deviceConfig['Contact Sensor'].maxRetries", + "options.deviceConfig['Contact Sensor'].delayBetweenRetries", + "options.deviceConfig['Contact Sensor'].disableCaching", + "options.deviceConfig['Contact Sensor'].offline", + "options.deviceConfig['Contact Sensor'].history", + "options.deviceConfig['Contact Sensor'].firmware", + "options.deviceConfig['Contact Sensor'].logging", + "options.deviceConfig['Contact Sensor'].refreshRate", + "options.deviceConfig['Contact Sensor'].updateRate", + "options.deviceConfig['Contact Sensor'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Curtain Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Curtain'].hide_device", + "options.deviceConfig['Curtain'].disable_group", + "options.deviceConfig['Curtain'].hide_lightsensor", + "options.deviceConfig['Curtain'].set_minlux", + "options.deviceConfig['Curtain'].set_maxlux", + "options.deviceConfig['Curtain'].set_max", + "options.deviceConfig['Curtain'].set_min", + "options.deviceConfig['Curtain'].set_minStep", + "options.deviceConfig['Curtain'].setCloseMode", + "options.deviceConfig['Curtain'].setOpenMode", + "options.deviceConfig['Curtain'].silentModeSwitch", + "options.deviceConfig['Curtain'].connectionType", + "options.deviceConfig['Curtain'].scanDuration", + "options.deviceConfig['Curtain'].webhook", + "options.deviceConfig['Curtain'].external", + "options.deviceConfig['Curtain'].maxRetry", + "options.deviceConfig['Curtain'].maxRetries", + "options.deviceConfig['Curtain'].delayBetweenRetries", + "options.deviceConfig['Curtain'].disableCaching", + "options.deviceConfig['Curtain'].offline", + "options.deviceConfig['Curtain'].history", + "options.deviceConfig['Curtain'].firmware", + "options.deviceConfig['Curtain'].logging", + "options.deviceConfig['Curtain'].refreshRate", + "options.deviceConfig['Curtain'].updateRate", + "options.deviceConfig['Curtain'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Curtain3 Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Curtain3'].hide_device", + "options.deviceConfig['Curtain3'].disable_group", + "options.deviceConfig['Curtain3'].hide_lightsensor", + "options.deviceConfig['Curtain3'].set_minlux", + "options.deviceConfig['Curtain3'].set_maxlux", + "options.deviceConfig['Curtain3'].set_max", + "options.deviceConfig['Curtain3'].set_min", + "options.deviceConfig['Curtain3'].set_minStep", + "options.deviceConfig['Curtain3'].setCloseMode", + "options.deviceConfig['Curtain3'].setOpenMode", + "options.deviceConfig['Curtain3'].silentModeSwitch", + "options.deviceConfig['Curtain3'].connectionType", + "options.deviceConfig['Curtain3'].scanDuration", + "options.deviceConfig['Curtain3'].webhook", + "options.deviceConfig['Curtain3'].external", + "options.deviceConfig['Curtain3'].maxRetry", + "options.deviceConfig['Curtain3'].maxRetries", + "options.deviceConfig['Curtain3'].delayBetweenRetries", + "options.deviceConfig['Curtain3'].disableCaching", + "options.deviceConfig['Curtain3'].offline", + "options.deviceConfig['Curtain3'].history", + "options.deviceConfig['Curtain3'].firmware", + "options.deviceConfig['Curtain3'].logging", + "options.deviceConfig['Curtain3'].refreshRate", + "options.deviceConfig['Curtain3'].updateRate", + "options.deviceConfig['Curtain3'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "WoRollerShade Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['WoRollerShade'].hide_device", + "options.deviceConfig['WoRollerShade'].disable_group", + "options.deviceConfig['WoRollerShade'].hide_lightsensor", + "options.deviceConfig['WoRollerShade'].set_minlux", + "options.deviceConfig['WoRollerShade'].set_maxlux", + "options.deviceConfig['WoRollerShade'].set_max", + "options.deviceConfig['WoRollerShade'].set_min", + "options.deviceConfig['WoRollerShade'].set_minStep", + "options.deviceConfig['WoRollerShade'].setCloseMode", + "options.deviceConfig['WoRollerShade'].setOpenMode", + "options.deviceConfig['WoRollerShade'].silentModeSwitch", + "options.deviceConfig['WoRollerShade'].connectionType", + "options.deviceConfig['WoRollerShade'].scanDuration", + "options.deviceConfig['WoRollerShade'].webhook", + "options.deviceConfig['WoRollerShade'].external", + "options.deviceConfig['WoRollerShade'].maxRetry", + "options.deviceConfig['WoRollerShade'].maxRetries", + "options.deviceConfig['WoRollerShade'].delayBetweenRetries", + "options.deviceConfig['WoRollerShade'].disableCaching", + "options.deviceConfig['WoRollerShade'].offline", + "options.deviceConfig['WoRollerShade'].history", + "options.deviceConfig['WoRollerShade'].firmware", + "options.deviceConfig['WoRollerShade'].logging", + "options.deviceConfig['WoRollerShade'].refreshRate", + "options.deviceConfig['WoRollerShade'].updateRate", + "options.deviceConfig['WoRollerShade'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Roller Shade Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Roller Shade'].hide_device", + "options.deviceConfig['Roller Shade'].disable_group", + "options.deviceConfig['Roller Shade'].hide_lightsensor", + "options.deviceConfig['Roller Shade'].set_minlux", + "options.deviceConfig['Roller Shade'].set_maxlux", + "options.deviceConfig['Roller Shade'].set_max", + "options.deviceConfig['Roller Shade'].set_min", + "options.deviceConfig['Roller Shade'].set_minStep", + "options.deviceConfig['Roller Shade'].setCloseMode", + "options.deviceConfig['Roller Shade'].setOpenMode", + "options.deviceConfig['Roller Shade'].silentModeSwitch", + "options.deviceConfig['Roller Shade'].connectionType", + "options.deviceConfig['Roller Shade'].scanDuration", + "options.deviceConfig['Roller Shade'].webhook", + "options.deviceConfig['Roller Shade'].external", + "options.deviceConfig['Roller Shade'].maxRetry", + "options.deviceConfig['Roller Shade'].maxRetries", + "options.deviceConfig['Roller Shade'].delayBetweenRetries", + "options.deviceConfig['Roller Shade'].disableCaching", + "options.deviceConfig['Roller Shade'].offline", + "options.deviceConfig['Roller Shade'].history", + "options.deviceConfig['Roller Shade'].firmware", + "options.deviceConfig['Roller Shade'].logging", + "options.deviceConfig['Roller Shade'].refreshRate", + "options.deviceConfig['Roller Shade'].updateRate", + "options.deviceConfig['Roller Shade'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Blind Tilt Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Blind Tilt'].hide_device", + "options.deviceConfig['Blind Tilt'].disable_group", + "options.deviceConfig['Blind Tilt'].mapping", + "options.deviceConfig['Blind Tilt'].hide_lightsensor", + "options.deviceConfig['Blind Tilt'].set_minlux", + "options.deviceConfig['Blind Tilt'].set_maxlux", + "options.deviceConfig['Blind Tilt'].set_max", + "options.deviceConfig['Blind Tilt'].set_min", + "options.deviceConfig['Blind Tilt'].set_minStep", + "options.deviceConfig['Blind Tilt'].setCloseMode", + "options.deviceConfig['Blind Tilt'].setOpenMode", + "options.deviceConfig['Blind Tilt'].silentModeSwitch", + "options.deviceConfig['Blind Tilt'].connectionType", + "options.deviceConfig['Blind Tilt'].scanDuration", + "options.deviceConfig['Blind Tilt'].webhook", + "options.deviceConfig['Blind Tilt'].external", + "options.deviceConfig['Blind Tilt'].maxRetry", + "options.deviceConfig['Blind Tilt'].maxRetries", + "options.deviceConfig['Blind Tilt'].delayBetweenRetries", + "options.deviceConfig['Blind Tilt'].disableCaching", + "options.deviceConfig['Blind Tilt'].offline", + "options.deviceConfig['Blind Tilt'].history", + "options.deviceConfig['Blind Tilt'].firmware", + "options.deviceConfig['Blind Tilt'].logging", + "options.deviceConfig['Blind Tilt'].refreshRate", + "options.deviceConfig['Blind Tilt'].updateRate", + "options.deviceConfig['Blind Tilt'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Plug Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Plug'].hide_device", + "options.deviceConfig['Plug'].connectionType", + "options.deviceConfig['Plug'].scanDuration", + "options.deviceConfig['Plug'].webhook", + "options.deviceConfig['Plug'].external", + "options.deviceConfig['Plug'].maxRetry", + "options.deviceConfig['Plug'].maxRetries", + "options.deviceConfig['Plug'].delayBetweenRetries", + "options.deviceConfig['Plug'].disableCaching", + "options.deviceConfig['Plug'].offline", + "options.deviceConfig['Plug'].history", + "options.deviceConfig['Plug'].firmware", + "options.deviceConfig['Plug'].logging", + "options.deviceConfig['Plug'].refreshRate", + "options.deviceConfig['Plug'].updateRate", + "options.deviceConfig['Plug'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Plug Mini (US) Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Plug Mini (US)'].hide_device", + "options.deviceConfig['Plug Mini (US)'].connectionType", + "options.deviceConfig['Plug Mini (US)'].scanDuration", + "options.deviceConfig['Plug Mini (US)'].webhook", + "options.deviceConfig['Plug Mini (US)'].external", + "options.deviceConfig['Plug Mini (US)'].maxRetry", + "options.deviceConfig['Plug Mini (US)'].maxRetries", + "options.deviceConfig['Plug Mini (US)'].delayBetweenRetries", + "options.deviceConfig['Plug Mini (US)'].disableCaching", + "options.deviceConfig['Plug Mini (US)'].offline", + "options.deviceConfig['Plug Mini (US)'].history", + "options.deviceConfig['Plug Mini (US)'].firmware", + "options.deviceConfig['Plug Mini (US)'].logging", + "options.deviceConfig['Plug Mini (US)'].refreshRate", + "options.deviceConfig['Plug Mini (US)'].updateRate", + "options.deviceConfig['Plug Mini (US)'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Plug Mini (JP) Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Plug Mini (JP)'].hide_device", + "options.deviceConfig['Plug Mini (JP)'].connectionType", + "options.deviceConfig['Plug Mini (JP)'].scanDuration", + "options.deviceConfig['Plug Mini (JP)'].webhook", + "options.deviceConfig['Plug Mini (JP)'].external", + "options.deviceConfig['Plug Mini (JP)'].maxRetry", + "options.deviceConfig['Plug Mini (JP)'].maxRetries", + "options.deviceConfig['Plug Mini (JP)'].delayBetweenRetries", + "options.deviceConfig['Plug Mini (JP)'].disableCaching", + "options.deviceConfig['Plug Mini (JP)'].offline", + "options.deviceConfig['Plug Mini (JP)'].history", + "options.deviceConfig['Plug Mini (JP)'].firmware", + "options.deviceConfig['Plug Mini (JP)'].logging", + "options.deviceConfig['Plug Mini (JP)'].refreshRate", + "options.deviceConfig['Plug Mini (JP)'].updateRate", + "options.deviceConfig['Plug Mini (JP)'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Smart Lock Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Smart Lock'].hide_device", + "options.deviceConfig['Smart Lock'].hide_contactsensor", + "options.deviceConfig['Smart Lock'].activate_latchbutton", + "options.deviceConfig['Smart Lock'].connectionType", + "options.deviceConfig['Smart Lock'].scanDuration", + "options.deviceConfig['Smart Lock'].webhook", + "options.deviceConfig['Smart Lock'].external", + "options.deviceConfig['Smart Lock'].maxRetry", + "options.deviceConfig['Smart Lock'].maxRetries", + "options.deviceConfig['Smart Lock'].delayBetweenRetries", + "options.deviceConfig['Smart Lock'].disableCaching", + "options.deviceConfig['Smart Lock'].offline", + "options.deviceConfig['Smart Lock'].history", + "options.deviceConfig['Smart Lock'].firmware", + "options.deviceConfig['Smart Lock'].logging", + "options.deviceConfig['Smart Lock'].refreshRate", + "options.deviceConfig['Smart Lock'].updateRate", + "options.deviceConfig['Smart Lock'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Smart Lock Pro Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Smart Lock Pro'].hide_device", + "options.deviceConfig['Smart Lock Pro'].hide_contactsensor", + "options.deviceConfig['Smart Lock Pro'].activate_latchbutton", + "options.deviceConfig['Smart Lock Pro'].connectionType", + "options.deviceConfig['Smart Lock Pro'].scanDuration", + "options.deviceConfig['Smart Lock Pro'].webhook", + "options.deviceConfig['Smart Lock Pro'].external", + "options.deviceConfig['Smart Lock Pro'].maxRetry", + "options.deviceConfig['Smart Lock Pro'].maxRetries", + "options.deviceConfig['Smart Lock Pro'].delayBetweenRetries", + "options.deviceConfig['Smart Lock Pro'].disableCaching", + "options.deviceConfig['Smart Lock Pro'].offline", + "options.deviceConfig['Smart Lock Pro'].history", + "options.deviceConfig['Smart Lock Pro'].firmware", + "options.deviceConfig['Smart Lock Pro'].logging", + "options.deviceConfig['Smart Lock Pro'].refreshRate", + "options.deviceConfig['Smart Lock Pro'].updateRate", + "options.deviceConfig['Smart Lock Pro'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Color Bulb Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Color Bulb'].hide_device", + "options.deviceConfig['Color Bulb'].set_minStep", + "options.deviceConfig['Color Bulb'].adaptiveLightingShift", + "options.deviceConfig['Color Bulb'].connectionType", + "options.deviceConfig['Color Bulb'].scanDuration", + "options.deviceConfig['Color Bulb'].webhook", + "options.deviceConfig['Color Bulb'].external", + "options.deviceConfig['Color Bulb'].maxRetry", + "options.deviceConfig['Color Bulb'].maxRetries", + "options.deviceConfig['Color Bulb'].delayBetweenRetries", + "options.deviceConfig['Color Bulb'].disableCaching", + "options.deviceConfig['Color Bulb'].offline", + "options.deviceConfig['Color Bulb'].history", + "options.deviceConfig['Color Bulb'].firmware", + "options.deviceConfig['Color Bulb'].logging", + "options.deviceConfig['Color Bulb'].refreshRate", + "options.deviceConfig['Color Bulb'].updateRate", + "options.deviceConfig['Color Bulb'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "K10+ Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['K10+'].hide_device", + "options.deviceConfig['K10+'].connectionType", + "options.deviceConfig['K10+'].scanDuration", + "options.deviceConfig['K10+'].webhook", + "options.deviceConfig['K10+'].external", + "options.deviceConfig['K10+'].maxRetry", + "options.deviceConfig['K10+'].maxRetries", + "options.deviceConfig['K10+'].delayBetweenRetries", + "options.deviceConfig['K10+'].disableCaching", + "options.deviceConfig['K10+'].offline", + "options.deviceConfig['K10+'].history", + "options.deviceConfig['K10+'].firmware", + "options.deviceConfig['K10+'].logging", + "options.deviceConfig['K10+'].refreshRate", + "options.deviceConfig['K10+'].updateRate", + "options.deviceConfig['K10+'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "K10+ Pro Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['K10+ Pro'].hide_device", + "options.deviceConfig['K10+ Pro'].connectionType", + "options.deviceConfig['K10+ Pro'].scanDuration", + "options.deviceConfig['K10+ Pro'].webhook", + "options.deviceConfig['K10+ Pro'].external", + "options.deviceConfig['K10+ Pro'].maxRetry", + "options.deviceConfig['K10+ Pro'].maxRetries", + "options.deviceConfig['K10+ Pro'].delayBetweenRetries", + "options.deviceConfig['K10+ Pro'].disableCaching", + "options.deviceConfig['K10+ Pro'].offline", + "options.deviceConfig['K10+ Pro'].history", + "options.deviceConfig['K10+ Pro'].firmware", + "options.deviceConfig['K10+ Pro'].logging", + "options.deviceConfig['K10+ Pro'].refreshRate", + "options.deviceConfig['K10+ Pro'].updateRate", + "options.deviceConfig['K10+ Pro'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "WoSweeper Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['WoSweeper'].hide_device", + "options.deviceConfig['WoSweeper'].connectionType", + "options.deviceConfig['WoSweeper'].scanDuration", + "options.deviceConfig['WoSweeper'].webhook", + "options.deviceConfig['WoSweeper'].external", + "options.deviceConfig['WoSweeper'].maxRetry", + "options.deviceConfig['WoSweeper'].maxRetries", + "options.deviceConfig['WoSweeper'].delayBetweenRetries", + "options.deviceConfig['WoSweeper'].disableCaching", + "options.deviceConfig['WoSweeper'].offline", + "options.deviceConfig['WoSweeper'].history", + "options.deviceConfig['WoSweeper'].firmware", + "options.deviceConfig['WoSweeper'].logging", + "options.deviceConfig['WoSweeper'].refreshRate", + "options.deviceConfig['WoSweeper'].updateRate", + "options.deviceConfig['WoSweeper'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "WoSweeperMini Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['WoSweeperMini'].hide_device", + "options.deviceConfig['WoSweeperMini'].connectionType", + "options.deviceConfig['WoSweeperMini'].scanDuration", + "options.deviceConfig['WoSweeperMini'].webhook", + "options.deviceConfig['WoSweeperMini'].external", + "options.deviceConfig['WoSweeperMini'].maxRetry", + "options.deviceConfig['WoSweeperMini'].maxRetries", + "options.deviceConfig['WoSweeperMini'].delayBetweenRetries", + "options.deviceConfig['WoSweeperMini'].disableCaching", + "options.deviceConfig['WoSweeperMini'].offline", + "options.deviceConfig['WoSweeperMini'].history", + "options.deviceConfig['WoSweeperMini'].firmware", + "options.deviceConfig['WoSweeperMini'].logging", + "options.deviceConfig['WoSweeperMini'].refreshRate", + "options.deviceConfig['WoSweeperMini'].updateRate", + "options.deviceConfig['WoSweeperMini'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Robot Vacuum Cleaner S1 Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Robot Vacuum Cleaner S1'].hide_device", + "options.deviceConfig['Robot Vacuum Cleaner S1'].connectionType", + "options.deviceConfig['Robot Vacuum Cleaner S1'].scanDuration", + "options.deviceConfig['Robot Vacuum Cleaner S1'].webhook", + "options.deviceConfig['Robot Vacuum Cleaner S1'].external", + "options.deviceConfig['Robot Vacuum Cleaner S1'].maxRetry", + "options.deviceConfig['Robot Vacuum Cleaner S1'].maxRetries", + "options.deviceConfig['Robot Vacuum Cleaner S1'].delayBetweenRetries", + "options.deviceConfig['Robot Vacuum Cleaner S1'].disableCaching", + "options.deviceConfig['Robot Vacuum Cleaner S1'].offline", + "options.deviceConfig['Robot Vacuum Cleaner S1'].history", + "options.deviceConfig['Robot Vacuum Cleaner S1'].firmware", + "options.deviceConfig['Robot Vacuum Cleaner S1'].logging", + "options.deviceConfig['Robot Vacuum Cleaner S1'].refreshRate", + "options.deviceConfig['Robot Vacuum Cleaner S1'].updateRate", + "options.deviceConfig['Robot Vacuum Cleaner S1'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Robot Vacuum Cleaner S1 Plus Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].hide_device", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].connectionType", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].scanDuration", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].webhook", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].external", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].maxRetry", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].maxRetries", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].delayBetweenRetries", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].disableCaching", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].offline", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].history", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].firmware", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].logging", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].refreshRate", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].updateRate", + "options.deviceConfig['Robot Vacuum Cleaner S1 Plus'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Robot Vacuum Cleaner S10 Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Robot Vacuum Cleaner S10'].hide_device", + "options.deviceConfig['Robot Vacuum Cleaner S10'].connectionType", + "options.deviceConfig['Robot Vacuum Cleaner S10'].scanDuration", + "options.deviceConfig['Robot Vacuum Cleaner S10'].webhook", + "options.deviceConfig['Robot Vacuum Cleaner S10'].external", + "options.deviceConfig['Robot Vacuum Cleaner S10'].maxRetry", + "options.deviceConfig['Robot Vacuum Cleaner S10'].maxRetries", + "options.deviceConfig['Robot Vacuum Cleaner S10'].delayBetweenRetries", + "options.deviceConfig['Robot Vacuum Cleaner S10'].disableCaching", + "options.deviceConfig['Robot Vacuum Cleaner S10'].offline", + "options.deviceConfig['Robot Vacuum Cleaner S10'].history", + "options.deviceConfig['Robot Vacuum Cleaner S10'].firmware", + "options.deviceConfig['Robot Vacuum Cleaner S10'].logging", + "options.deviceConfig['Robot Vacuum Cleaner S10'].refreshRate", + "options.deviceConfig['Robot Vacuum Cleaner S10'].updateRate", + "options.deviceConfig['Robot Vacuum Cleaner S10'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Ceiling Light Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Ceiling Light'].hide_device", + "options.deviceConfig['Ceiling Light'].set_minStep", + "options.deviceConfig['Ceiling Light'].adaptiveLightingShift", + "options.deviceConfig['Ceiling Light'].connectionType", + "options.deviceConfig['Ceiling Light'].scanDuration", + "options.deviceConfig['Ceiling Light'].webhook", + "options.deviceConfig['Ceiling Light'].external", + "options.deviceConfig['Ceiling Light'].maxRetry", + "options.deviceConfig['Ceiling Light'].maxRetries", + "options.deviceConfig['Ceiling Light'].delayBetweenRetries", + "options.deviceConfig['Ceiling Light'].disableCaching", + "options.deviceConfig['Ceiling Light'].offline", + "options.deviceConfig['Ceiling Light'].history", + "options.deviceConfig['Ceiling Light'].firmware", + "options.deviceConfig['Ceiling Light'].logging", + "options.deviceConfig['Ceiling Light'].refreshRate", + "options.deviceConfig['Ceiling Light'].updateRate", + "options.deviceConfig['Ceiling Light'].pushRate" + ] + }, { - "key": "options.devices", - "notitle": false, - "type": "tabarray", - "title": "{{ value.configDeviceName || value.deviceId || 'New SwitchBot Device' }}", + "type": "fieldset", + "title": "Ceiling Light Pro Settings", "expandable": true, "expanded": false, - "draggable": true, - "orderable": true, "items": [ - "options.devices[].configDeviceName", - "options.devices[].deviceId", - "options.devices[].hide_device", - "options.devices[].configDeviceType", - "options.devices[].connectionType", - "options.devices[].webhook", - "options.devices[].bot.deviceType", - "options.devices[].bot.mode", - "options.devices[].bot.allowPush", - "options.devices[].bot.doublePress", - "options.devices[].bot.pushRatePress", - "options.devices[].meter.hide_temperature", - "options.devices[].meter.convertUnitTo", - "options.devices[].meter.hide_humidity", - "options.devices[].iosensor.hide_temperature", - "options.devices[].iosensor.convertUnitTo", - "options.devices[].iosensor.hide_humidity", - "options.devices[].hub.hide_temperature", - "options.devices[].hub.convertUnitTo", - "options.devices[].hub.hide_humidity", - "options.devices[].hub.hide_lightsensor", - "options.devices[].waterdetector.hide_leak", - "options.devices[].waterdetector.dry", - "options.devices[].humidifier.set_minStep", - "options.devices[].humidifier.hide_temperature", - "options.devices[].curtain.set_minStep", - "options.devices[].curtain.set_min", - "options.devices[].curtain.set_max", - "options.devices[].curtain.setOpenMode", - "options.devices[].curtain.setCloseMode", - "options.devices[].curtain.silentModeSwitch", - "options.devices[].curtain.updateRate", - "options.devices[].curtain.disable_group", - "options.devices[].curtain.hide_lightsensor", - "options.devices[].curtain.set_minlux", - "options.devices[].curtain.set_maxlux", - "options.devices[].blindTilt.mode", - "options.devices[].blindTilt.set_minStep", - "options.devices[].blindTilt.set_min", - "options.devices[].blindTilt.set_max", - "options.devices[].blindTilt.hide_lightsensor", - "options.devices[].blindTilt.updateRate", - "options.devices[].blindTilt.setOpenMode", - "options.devices[].blindTilt.setCloseMode", - "options.devices[].blindTilt.silentModeSwitch", - "options.devices[].contact.hide_lightsensor", - "options.devices[].contact.set_minlux", - "options.devices[].contact.set_maxlux", - "options.devices[].contact.hide_motionsensor", - "options.devices[].motion.hide_lightsensor", - "options.devices[].motion.set_minlux", - "options.devices[].motion.set_maxlux", - "options.devices[].colorbulb.set_minStep", - "options.devices[].colorbulb.adaptiveLightingShift", - "options.devices[].striplight.set_minStep", - "options.devices[].striplight.adaptiveLightingShift", - "options.devices[].lock.hide_contactsensor", - "options.devices[].lock.activate_latchbutton", - "options.devices[].disableCaching", - "options.devices[].maxRetries", - "options.devices[].delayBetweenRetries", - "options.devices[].maxRetry", - "options.devices[].mqttURL", - "options.devices[].mqttOptions", - "options.devices[].mqttPubOptions", - "options.devices[].history", - "options.devices[].firmware", - "options.devices[].scanDuration", - { - "key": "options.devices[].refreshRate", - "description": "Specifies the interval, in seconds, for retrieving the latest device status from the SwitchBot API. This interval applies only to this specific device." - }, - { - "key": "options.devices[].updateRate", - "description": "Specifies the interval, in seconds, at which this device will request updates from the SwitchBot API while the device is in motion, for Curtain(s) and Blind Tilt(s) only." - }, - { - "key": "options.devices[].pushRate", - "description": "Specifies the interval, in seconds, between pushes to the SwitchBot API for this specific device." - }, - "options.devices[].offline", - "options.devices[].external", - "options.devices[].logging" + "options.deviceConfig['Ceiling Light Pro'].hide_device", + "options.deviceConfig['Ceiling Light Pro'].set_minStep", + "options.deviceConfig['Ceiling Light Pro'].adaptiveLightingShift", + "options.deviceConfig['Ceiling Light Pro'].connectionType", + "options.deviceConfig['Ceiling Light Pro'].scanDuration", + "options.deviceConfig['Ceiling Light Pro'].webhook", + "options.deviceConfig['Ceiling Light Pro'].external", + "options.deviceConfig['Ceiling Light Pro'].maxRetry", + "options.deviceConfig['Ceiling Light Pro'].maxRetries", + "options.deviceConfig['Ceiling Light Pro'].delayBetweenRetries", + "options.deviceConfig['Ceiling Light Pro'].disableCaching", + "options.deviceConfig['Ceiling Light Pro'].offline", + "options.deviceConfig['Ceiling Light Pro'].history", + "options.deviceConfig['Ceiling Light Pro'].firmware", + "options.deviceConfig['Ceiling Light Pro'].logging", + "options.deviceConfig['Ceiling Light Pro'].refreshRate", + "options.deviceConfig['Ceiling Light Pro'].updateRate", + "options.deviceConfig['Ceiling Light Pro'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Strip Light Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Strip Light'].hide_device", + "options.deviceConfig['Strip Light'].set_minStep", + "options.deviceConfig['Strip Light'].adaptiveLightingShift", + "options.deviceConfig['Strip Light'].connectionType", + "options.deviceConfig['Strip Light'].scanDuration", + "options.deviceConfig['Strip Light'].webhook", + "options.deviceConfig['Strip Light'].external", + "options.deviceConfig['Strip Light'].maxRetry", + "options.deviceConfig['Strip Light'].maxRetries", + "options.deviceConfig['Strip Light'].delayBetweenRetries", + "options.deviceConfig['Strip Light'].disableCaching", + "options.deviceConfig['Strip Light'].offline", + "options.deviceConfig['Strip Light'].history", + "options.deviceConfig['Strip Light'].firmware", + "options.deviceConfig['Strip Light'].logging", + "options.deviceConfig['Strip Light'].refreshRate", + "options.deviceConfig['Strip Light'].updateRate", + "options.deviceConfig['Strip Light'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Battery Circulator Fan Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.deviceConfig['Battery Circulator Fan'].hide_device", + "options.deviceConfig['Battery Circulator Fan'].set_minStep", + "options.deviceConfig['Battery Circulator Fan'].connectionType", + "options.deviceConfig['Battery Circulator Fan'].scanDuration", + "options.deviceConfig['Battery Circulator Fan'].webhook", + "options.deviceConfig['Battery Circulator Fan'].external", + "options.deviceConfig['Battery Circulator Fan'].maxRetry", + "options.deviceConfig['Battery Circulator Fan'].maxRetries", + "options.deviceConfig['Battery Circulator Fan'].delayBetweenRetries", + "options.deviceConfig['Battery Circulator Fan'].disableCaching", + "options.deviceConfig['Battery Circulator Fan'].offline", + "options.deviceConfig['Battery Circulator Fan'].history", + "options.deviceConfig['Battery Circulator Fan'].firmware", + "options.deviceConfig['Battery Circulator Fan'].logging", + "options.deviceConfig['Battery Circulator Fan'].refreshRate", + "options.deviceConfig['Battery Circulator Fan'].updateRate", + "options.deviceConfig['Battery Circulator Fan'].pushRate" ] } ] @@ -1941,6 +12846,10 @@ "expandable": true, "expanded": false, "items": [ + { + "type": "help", + "helpvalue": "With IR Device Setting, you can set device specific settings based on deviceId. This will override IR Remote Type Settings" + }, { "key": "options.irdevices", "notitle": false, @@ -1956,7 +12865,7 @@ "options.irdevices[].hide_device", "options.irdevices[].configRemoteType", "options.irdevices[].connectionType", - "options.irdevices[].other.deviceType", + "options.irdevices[].type", "options.irdevices[].customize", "options.irdevices[].customOn", "options.irdevices[].customOff", @@ -1964,22 +12873,553 @@ "options.irdevices[].disablePushOn", "options.irdevices[].disablePushOff", "options.irdevices[].disablePushDetail", - "options.irdevices[].irair.hide_automode", - "options.irdevices[].irair.set_max_heat", - "options.irdevices[].irair.set_min_heat", - "options.irdevices[].irair.set_max_cool", - "options.irdevices[].irair.set_min_cool", - "options.irdevices[].irair.meterType", - "options.irdevices[].irair.meterId", - "options.irdevices[].irfan.rotation_speed", - "options.irdevices[].irfan.swing_mode", - "options.irdevices[].irfan.set_minStep", - "options.irdevices[].irfan.set_min", - "options.irdevices[].irfan.set_max", - "options.irdevices[].irlight.stateless", - "options.irdevices[].firmware", + "options.irdevices[].hide_automode", + "options.irdevices[].set_max_heat", + "options.irdevices[].set_min_heat", + "options.irdevices[].set_max_cool", + "options.irdevices[].set_min_cool", + "options.irdevices[].meterType", + "options.irdevices[].meterId", + "options.irdevices[].rotation_speed", + "options.irdevices[].swing_mode", + "options.irdevices[].set_minStep", + "options.irdevices[].set_min", + "options.irdevices[].set_max", + "options.irdevices[].stateless", "options.irdevices[].external", - "options.irdevices[].logging" + "options.irdevices[].disableCaching", + "options.irdevices[].offline", + "options.irdevices[].history", + "options.irdevices[].firmware", + "options.irdevices[].logging", + "options.irdevices[].refreshRate", + "options.irdevices[].updateRate", + "options.irdevices[].pushRate" + ] + } + ] + }, + { + "type": "fieldset", + "title": "IR Remote Type Settings", + "expandable": true, + "expanded": false, + "items": [ + { + "type": "help", + "helpvalue": "Setting an IR Remote Type Setting will not override indiviudally set setting on IR Device Settings." + }, + { + "type": "fieldset", + "title": "TV Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['TV'].hide_device", + "options.irdeviceConfig['TV'].connectionType", + "options.irdeviceConfig['TV'].external", + "options.irdeviceConfig['TV'].disableCaching", + "options.irdeviceConfig['TV'].offline", + "options.irdeviceConfig['TV'].history", + "options.irdeviceConfig['TV'].firmware", + "options.irdeviceConfig['TV'].logging", + "options.irdeviceConfig['TV'].refreshRate", + "options.irdeviceConfig['TV'].updateRate", + "options.irdeviceConfig['TV'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "DIY TV Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['DIY TV'].hide_device", + "options.irdeviceConfig['DIY TV'].connectionType", + "options.irdeviceConfig['DIY TV'].external", + "options.irdeviceConfig['DIY TV'].disableCaching", + "options.irdeviceConfig['DIY TV'].offline", + "options.irdeviceConfig['DIY TV'].history", + "options.irdeviceConfig['DIY TV'].firmware", + "options.irdeviceConfig['DIY TV'].logging", + "options.irdeviceConfig['DIY TV'].refreshRate", + "options.irdeviceConfig['DIY TV'].updateRate", + "options.irdeviceConfig['DIY TV'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Projector Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['Projector'].hide_device", + "options.irdeviceConfig['Projector'].connectionType", + "options.irdeviceConfig['Projector'].external", + "options.irdeviceConfig['Projector'].disableCaching", + "options.irdeviceConfig['Projector'].offline", + "options.irdeviceConfig['Projector'].history", + "options.irdeviceConfig['Projector'].firmware", + "options.irdeviceConfig['Projector'].logging", + "options.irdeviceConfig['Projector'].refreshRate", + "options.irdeviceConfig['Projector'].updateRate", + "options.irdeviceConfig['Projector'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "DIY Projector Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['DIY Projector'].hide_device", + "options.irdeviceConfig['DIY Projector'].connectionType", + "options.irdeviceConfig['DIY Projector'].external", + "options.irdeviceConfig['DIY Projector'].disableCaching", + "options.irdeviceConfig['DIY Projector'].offline", + "options.irdeviceConfig['DIY Projector'].history", + "options.irdeviceConfig['DIY Projector'].firmware", + "options.irdeviceConfig['DIY Projector'].logging", + "options.irdeviceConfig['DIY Projector'].refreshRate", + "options.irdeviceConfig['DIY Projector'].updateRate", + "options.irdeviceConfig['DIY Projector'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Set Top Box Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['Set Top Box'].hide_device", + "options.irdeviceConfig['Set Top Box'].connectionType", + "options.irdeviceConfig['Set Top Box'].external", + "options.irdeviceConfig['Set Top Box'].disableCaching", + "options.irdeviceConfig['Set Top Box'].offline", + "options.irdeviceConfig['Set Top Box'].history", + "options.irdeviceConfig['Set Top Box'].firmware", + "options.irdeviceConfig['Set Top Box'].logging", + "options.irdeviceConfig['Set Top Box'].refreshRate", + "options.irdeviceConfig['Set Top Box'].updateRate", + "options.irdeviceConfig['Set Top Box'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "DIY Set Top Box Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['DIY Set Top Box'].hide_device", + "options.irdeviceConfig['DIY Set Top Box'].connectionType", + "options.irdeviceConfig['DIY Set Top Box'].external", + "options.irdeviceConfig['DIY Set Top Box'].disableCaching", + "options.irdeviceConfig['DIY Set Top Box'].offline", + "options.irdeviceConfig['DIY Set Top Box'].history", + "options.irdeviceConfig['DIY Set Top Box'].firmware", + "options.irdeviceConfig['DIY Set Top Box'].logging", + "options.irdeviceConfig['DIY Set Top Box'].refreshRate", + "options.irdeviceConfig['DIY Set Top Box'].updateRate", + "options.irdeviceConfig['DIY Set Top Box'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "IPTV Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['IPTV'].hide_device", + "options.irdeviceConfig['IPTV'].connectionType", + "options.irdeviceConfig['IPTV'].external", + "options.irdeviceConfig['IPTV'].disableCaching", + "options.irdeviceConfig['IPTV'].offline", + "options.irdeviceConfig['IPTV'].history", + "options.irdeviceConfig['IPTV'].firmware", + "options.irdeviceConfig['IPTV'].logging", + "options.irdeviceConfig['IPTV'].refreshRate", + "options.irdeviceConfig['IPTV'].updateRate", + "options.irdeviceConfig['IPTV'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "DIY IPTV Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['DIY IPTV'].hide_device", + "options.irdeviceConfig['DIY IPTV'].connectionType", + "options.irdeviceConfig['DIY IPTV'].external", + "options.irdeviceConfig['DIY IPTV'].disableCaching", + "options.irdeviceConfig['DIY IPTV'].offline", + "options.irdeviceConfig['DIY IPTV'].history", + "options.irdeviceConfig['DIY IPTV'].firmware", + "options.irdeviceConfig['DIY IPTV'].logging", + "options.irdeviceConfig['DIY IPTV'].refreshRate", + "options.irdeviceConfig['DIY IPTV'].updateRate", + "options.irdeviceConfig['DIY IPTV'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "DVD Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['DVD'].hide_device", + "options.irdeviceConfig['DVD'].connectionType", + "options.irdeviceConfig['DVD'].external", + "options.irdeviceConfig['DVD'].disableCaching", + "options.irdeviceConfig['DVD'].offline", + "options.irdeviceConfig['DVD'].history", + "options.irdeviceConfig['DVD'].firmware", + "options.irdeviceConfig['DVD'].logging", + "options.irdeviceConfig['DVD'].refreshRate", + "options.irdeviceConfig['DVD'].updateRate", + "options.irdeviceConfig['DVD'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "DIY DVD Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['DIY DVD'].hide_device", + "options.irdeviceConfig['DIY DVD'].connectionType", + "options.irdeviceConfig['DIY DVD'].external", + "options.irdeviceConfig['DIY DVD'].disableCaching", + "options.irdeviceConfig['DIY DVD'].offline", + "options.irdeviceConfig['DIY DVD'].history", + "options.irdeviceConfig['DIY DVD'].firmware", + "options.irdeviceConfig['DIY DVD'].logging", + "options.irdeviceConfig['DIY DVD'].refreshRate", + "options.irdeviceConfig['DIY DVD'].updateRate", + "options.irdeviceConfig['DIY DVD'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Speaker Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['Speaker'].hide_device", + "options.irdeviceConfig['Speaker'].connectionType", + "options.irdeviceConfig['Speaker'].external", + "options.irdeviceConfig['Speaker'].disableCaching", + "options.irdeviceConfig['Speaker'].offline", + "options.irdeviceConfig['Speaker'].history", + "options.irdeviceConfig['Speaker'].firmware", + "options.irdeviceConfig['Speaker'].logging", + "options.irdeviceConfig['Speaker'].refreshRate", + "options.irdeviceConfig['Speaker'].updateRate", + "options.irdeviceConfig['Speaker'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "DIY Speaker Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['DIY Speaker'].hide_device", + "options.irdeviceConfig['DIY Speaker'].connectionType", + "options.irdeviceConfig['DIY Speaker'].external", + "options.irdeviceConfig['DIY Speaker'].disableCaching", + "options.irdeviceConfig['DIY Speaker'].offline", + "options.irdeviceConfig['DIY Speaker'].history", + "options.irdeviceConfig['DIY Speaker'].firmware", + "options.irdeviceConfig['DIY Speaker'].logging", + "options.irdeviceConfig['DIY Speaker'].refreshRate", + "options.irdeviceConfig['DIY Speaker'].updateRate", + "options.irdeviceConfig['DIY Speaker'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Fan Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['Fan'].hide_device", + "options.irdeviceConfig['Fan'].connectionType", + "options.irdeviceConfig['Fan'].external", + "options.irdeviceConfig['Fan'].disableCaching", + "options.irdeviceConfig['Fan'].offline", + "options.irdeviceConfig['Fan'].history", + "options.irdeviceConfig['Fan'].firmware", + "options.irdeviceConfig['Fan'].logging", + "options.irdeviceConfig['Fan'].refreshRate", + "options.irdeviceConfig['Fan'].updateRate", + "options.irdeviceConfig['Fan'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "DIY Fan Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['DIY Fan'].hide_device", + "options.irdeviceConfig['DIY Fan'].connectionType", + "options.irdeviceConfig['DIY Fan'].external", + "options.irdeviceConfig['DIY Fan'].disableCaching", + "options.irdeviceConfig['DIY Fan'].offline", + "options.irdeviceConfig['DIY Fan'].history", + "options.irdeviceConfig['DIY Fan'].firmware", + "options.irdeviceConfig['DIY Fan'].logging", + "options.irdeviceConfig['DIY Fan'].refreshRate", + "options.irdeviceConfig['DIY Fan'].updateRate", + "options.irdeviceConfig['DIY Fan'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Air Conditioner Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['Air Conditioner'].hide_device", + "options.irdeviceConfig['Air Conditioner'].connectionType", + "options.irdeviceConfig['Air Conditioner'].external", + "options.irdeviceConfig['Air Conditioner'].disableCaching", + "options.irdeviceConfig['Air Conditioner'].offline", + "options.irdeviceConfig['Air Conditioner'].history", + "options.irdeviceConfig['Air Conditioner'].firmware", + "options.irdeviceConfig['Air Conditioner'].logging", + "options.irdeviceConfig['Air Conditioner'].refreshRate", + "options.irdeviceConfig['Air Conditioner'].updateRate", + "options.irdeviceConfig['Air Conditioner'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "DIY Air Conditioner Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['DIY Air Conditioner'].hide_device", + "options.irdeviceConfig['DIY Air Conditioner'].connectionType", + "options.irdeviceConfig['DIY Air Conditioner'].external", + "options.irdeviceConfig['DIY Air Conditioner'].disableCaching", + "options.irdeviceConfig['DIY Air Conditioner'].offline", + "options.irdeviceConfig['DIY Air Conditioner'].history", + "options.irdeviceConfig['DIY Air Conditioner'].firmware", + "options.irdeviceConfig['DIY Air Conditioner'].logging", + "options.irdeviceConfig['DIY Air Conditioner'].refreshRate", + "options.irdeviceConfig['DIY Air Conditioner'].updateRate", + "options.irdeviceConfig['DIY Air Conditioner'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Light Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['Light'].hide_device", + "options.irdeviceConfig['Light'].connectionType", + "options.irdeviceConfig['Light'].external", + "options.irdeviceConfig['Light'].disableCaching", + "options.irdeviceConfig['Light'].offline", + "options.irdeviceConfig['Light'].history", + "options.irdeviceConfig['Light'].firmware", + "options.irdeviceConfig['Light'].logging", + "options.irdeviceConfig['Light'].refreshRate", + "options.irdeviceConfig['Light'].updateRate", + "options.irdeviceConfig['Light'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "DIY Light Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['DIY Light'].hide_device", + "options.irdeviceConfig['DIY Light'].connectionType", + "options.irdeviceConfig['DIY Light'].external", + "options.irdeviceConfig['DIY Light'].disableCaching", + "options.irdeviceConfig['DIY Light'].offline", + "options.irdeviceConfig['DIY Light'].history", + "options.irdeviceConfig['DIY Light'].firmware", + "options.irdeviceConfig['DIY Light'].logging", + "options.irdeviceConfig['DIY Light'].refreshRate", + "options.irdeviceConfig['DIY Light'].updateRate", + "options.irdeviceConfig['DIY Light'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Air Purifier Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['Air Purifier'].hide_device", + "options.irdeviceConfig['Air Purifier'].connectionType", + "options.irdeviceConfig['Air Purifier'].external", + "options.irdeviceConfig['Air Purifier'].disableCaching", + "options.irdeviceConfig['Air Purifier'].offline", + "options.irdeviceConfig['Air Purifier'].history", + "options.irdeviceConfig['Air Purifier'].firmware", + "options.irdeviceConfig['Air Purifier'].logging", + "options.irdeviceConfig['Air Purifier'].refreshRate", + "options.irdeviceConfig['Air Purifier'].updateRate", + "options.irdeviceConfig['Air Purifier'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "DIY Air Purifier Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['DIY Air Purifier'].hide_device", + "options.irdeviceConfig['DIY Air Purifier'].connectionType", + "options.irdeviceConfig['DIY Air Purifier'].external", + "options.irdeviceConfig['DIY Air Purifier'].disableCaching", + "options.irdeviceConfig['DIY Air Purifier'].offline", + "options.irdeviceConfig['DIY Air Purifier'].history", + "options.irdeviceConfig['DIY Air Purifier'].firmware", + "options.irdeviceConfig['DIY Air Purifier'].logging", + "options.irdeviceConfig['DIY Air Purifier'].refreshRate", + "options.irdeviceConfig['DIY Air Purifier'].updateRate", + "options.irdeviceConfig['DIY Air Purifier'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Water Heater Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['Water Heater'].hide_device", + "options.irdeviceConfig['Water Heater'].connectionType", + "options.irdeviceConfig['Water Heater'].external", + "options.irdeviceConfig['Water Heater'].disableCaching", + "options.irdeviceConfig['Water Heater'].offline", + "options.irdeviceConfig['Water Heater'].history", + "options.irdeviceConfig['Water Heater'].firmware", + "options.irdeviceConfig['Water Heater'].logging", + "options.irdeviceConfig['Water Heater'].refreshRate", + "options.irdeviceConfig['Water Heater'].updateRate", + "options.irdeviceConfig['Water Heater'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "DIY Water Heater Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['DIY Water Heater'].hide_device", + "options.irdeviceConfig['DIY Water Heater'].connectionType", + "options.irdeviceConfig['DIY Water Heater'].external", + "options.irdeviceConfig['DIY Water Heater'].disableCaching", + "options.irdeviceConfig['DIY Water Heater'].offline", + "options.irdeviceConfig['DIY Water Heater'].history", + "options.irdeviceConfig['DIY Water Heater'].firmware", + "options.irdeviceConfig['DIY Water Heater'].logging", + "options.irdeviceConfig['DIY Water Heater'].refreshRate", + "options.irdeviceConfig['DIY Water Heater'].updateRate", + "options.irdeviceConfig['DIY Water Heater'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Vacuum Cleaner Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['Vacuum Cleaner'].hide_device", + "options.irdeviceConfig['Vacuum Cleaner'].connectionType", + "options.irdeviceConfig['Vacuum Cleaner'].external", + "options.irdeviceConfig['Vacuum Cleaner'].disableCaching", + "options.irdeviceConfig['Vacuum Cleaner'].offline", + "options.irdeviceConfig['Vacuum Cleaner'].history", + "options.irdeviceConfig['Vacuum Cleaner'].firmware", + "options.irdeviceConfig['Vacuum Cleaner'].logging", + "options.irdeviceConfig['Vacuum Cleaner'].refreshRate", + "options.irdeviceConfig['Vacuum Cleaner'].updateRate", + "options.irdeviceConfig['Vacuum Cleaner'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "DIY Vacuum Cleaner Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['DIY Vacuum Cleaner'].hide_device", + "options.irdeviceConfig['DIY Vacuum Cleaner'].connectionType", + "options.irdeviceConfig['DIY Vacuum Cleaner'].external", + "options.irdeviceConfig['DIY Vacuum Cleaner'].disableCaching", + "options.irdeviceConfig['DIY Vacuum Cleaner'].offline", + "options.irdeviceConfig['DIY Vacuum Cleaner'].history", + "options.irdeviceConfig['DIY Vacuum Cleaner'].firmware", + "options.irdeviceConfig['DIY Vacuum Cleaner'].logging", + "options.irdeviceConfig['DIY Vacuum Cleaner'].refreshRate", + "options.irdeviceConfig['DIY Vacuum Cleaner'].updateRate", + "options.irdeviceConfig['DIY Vacuum Cleaner'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Camera Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['Camera'].hide_device", + "options.irdeviceConfig['Camera'].connectionType", + "options.irdeviceConfig['Camera'].external", + "options.irdeviceConfig['Camera'].disableCaching", + "options.irdeviceConfig['Camera'].offline", + "options.irdeviceConfig['Camera'].history", + "options.irdeviceConfig['Camera'].firmware", + "options.irdeviceConfig['Camera'].logging", + "options.irdeviceConfig['Camera'].refreshRate", + "options.irdeviceConfig['Camera'].updateRate", + "options.irdeviceConfig['Camera'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "DIY Camera Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['DIY Camera'].hide_device", + "options.irdeviceConfig['DIY Camera'].connectionType", + "options.irdeviceConfig['DIY Camera'].external", + "options.irdeviceConfig['DIY Camera'].disableCaching", + "options.irdeviceConfig['DIY Camera'].offline", + "options.irdeviceConfig['DIY Camera'].history", + "options.irdeviceConfig['DIY Camera'].firmware", + "options.irdeviceConfig['DIY Camera'].logging", + "options.irdeviceConfig['DIY Camera'].refreshRate", + "options.irdeviceConfig['DIY Camera'].updateRate", + "options.irdeviceConfig['DIY Camera'].pushRate" + ] + }, + { + "type": "fieldset", + "title": "Others Settings", + "expandable": true, + "expanded": false, + "items": [ + "options.irdeviceConfig['Others'].hide_device", + "options.irdeviceConfig['Others'].connectionType", + "options.irdeviceConfig['Others'].external", + "options.irdeviceConfig['Others'].disableCaching", + "options.irdeviceConfig['Others'].offline", + "options.irdeviceConfig['Others'].history", + "options.irdeviceConfig['Others'].firmware", + "options.irdeviceConfig['Others'].logging", + "options.irdeviceConfig['Others'].refreshRate", + "options.irdeviceConfig['Others'].updateRate", + "options.irdeviceConfig['Others'].pushRate" ] } ] @@ -1991,6 +13431,18 @@ "expanded": false, "items": [ "options.BLE", + { + "key": "options.discoverBLE", + "description": "This will scan for BLE devices and display an Array in the logs so you can add them to the config." + }, + { + "key": "options.disableLogsforBLE", + "description": "This will disable the logs coming from node-switchbot OpenAPI." + }, "options.webhookURL", "options.mqttURL", "options.mqttOptions", diff --git a/docs/assets/highlight.css b/docs/assets/highlight.css index 5674cf39..e77b4203 100644 --- a/docs/assets/highlight.css +++ b/docs/assets/highlight.css @@ -1,22 +1,64 @@ :root { + --light-hl-0: #000000; + --dark-hl-0: #C8C8C8; + --light-hl-1: #000000; + --dark-hl-1: #D4D4D4; + --light-hl-2: #001080; + --dark-hl-2: #9CDCFE; + --light-hl-3: #267F99; + --dark-hl-3: #4EC9B0; + --light-hl-4: #008000; + --dark-hl-4: #6A9955; + --light-hl-5: #098658; + --dark-hl-5: #B5CEA8; --light-code-background: #FFFFFF; --dark-code-background: #1E1E1E; } @media (prefers-color-scheme: light) { :root { + --hl-0: var(--light-hl-0); + --hl-1: var(--light-hl-1); + --hl-2: var(--light-hl-2); + --hl-3: var(--light-hl-3); + --hl-4: var(--light-hl-4); + --hl-5: var(--light-hl-5); --code-background: var(--light-code-background); } } @media (prefers-color-scheme: dark) { :root { + --hl-0: var(--dark-hl-0); + --hl-1: var(--dark-hl-1); + --hl-2: var(--dark-hl-2); + --hl-3: var(--dark-hl-3); + --hl-4: var(--dark-hl-4); + --hl-5: var(--dark-hl-5); --code-background: var(--dark-code-background); } } :root[data-theme='light'] { + --hl-0: var(--light-hl-0); + --hl-1: var(--light-hl-1); + --hl-2: var(--light-hl-2); + --hl-3: var(--light-hl-3); + --hl-4: var(--light-hl-4); + --hl-5: var(--light-hl-5); --code-background: var(--light-code-background); } :root[data-theme='dark'] { + --hl-0: var(--dark-hl-0); + --hl-1: var(--dark-hl-1); + --hl-2: var(--dark-hl-2); + --hl-3: var(--dark-hl-3); + --hl-4: var(--dark-hl-4); + --hl-5: var(--dark-hl-5); --code-background: var(--dark-code-background); } +.hl-0 { color: var(--hl-0); } +.hl-1 { color: var(--hl-1); } +.hl-2 { color: var(--hl-2); } +.hl-3 { color: var(--hl-3); } +.hl-4 { color: var(--hl-4); } +.hl-5 { color: var(--hl-5); } pre, code { background: var(--code-background); } diff --git a/docs/assets/main.js b/docs/assets/main.js index 35728810..21a5d74d 100644 --- a/docs/assets/main.js +++ b/docs/assets/main.js @@ -3,7 +3,7 @@ window.translations={"copy":"Copy","copied":"Copied!","normally_hidden":"This me "use strict";(()=>{var Pe=Object.create;var ie=Object.defineProperty;var Oe=Object.getOwnPropertyDescriptor;var _e=Object.getOwnPropertyNames;var Re=Object.getPrototypeOf,Me=Object.prototype.hasOwnProperty;var Fe=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var De=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of _e(e))!Me.call(t,i)&&i!==n&&ie(t,i,{get:()=>e[i],enumerable:!(r=Oe(e,i))||r.enumerable});return t};var Ae=(t,e,n)=>(n=t!=null?Pe(Re(t)):{},De(e||!t||!t.__esModule?ie(n,"default",{value:t,enumerable:!0}):n,t));var ue=Fe((ae,le)=>{(function(){var t=function(e){var n=new t.Builder;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),n.searchPipeline.add(t.stemmer),e.call(n,n),n.build()};t.version="2.3.9";t.utils={},t.utils.warn=function(e){return function(n){e.console&&console.warn&&console.warn(n)}}(this),t.utils.asString=function(e){return e==null?"":e.toString()},t.utils.clone=function(e){if(e==null)return e;for(var n=Object.create(null),r=Object.keys(e),i=0;i0){var d=t.utils.clone(n)||{};d.position=[a,u],d.index=s.length,s.push(new t.Token(r.slice(a,o),d))}a=o+1}}return s},t.tokenizer.separator=/[\s\-]+/;t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions=Object.create(null),t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. `,e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(r){var i=t.Pipeline.registeredFunctions[r];if(i)n.add(i);else throw new Error("Cannot load unregistered function: "+r)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(n){t.Pipeline.warnIfFunctionNotRegistered(n),this._stack.push(n)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");r=r+1,this._stack.splice(r,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");this._stack.splice(r,0,n)},t.Pipeline.prototype.remove=function(e){var n=this._stack.indexOf(e);n!=-1&&this._stack.splice(n,1)},t.Pipeline.prototype.run=function(e){for(var n=this._stack.length,r=0;r1&&(oe&&(r=s),o!=e);)i=r-n,s=n+Math.floor(i/2),o=this.elements[s*2];if(o==e||o>e)return s*2;if(ol?d+=2:a==l&&(n+=r[u+1]*i[d+1],u+=2,d+=2);return n},t.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},t.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),n=1,r=0;n0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new t.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new t.TokenSet;s.node.edges["*"]=l}if(s.str.length==0&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var u=s.node.edges["*"];else{var u=new t.TokenSet;s.node.edges["*"]=u}s.str.length==1&&(u.final=!0),i.push({node:u,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var d=s.str.charAt(0),m=s.str.charAt(1),p;m in s.node.edges?p=s.node.edges[m]:(p=new t.TokenSet,s.node.edges[m]=p),s.str.length==1&&(p.final=!0),i.push({node:p,editsRemaining:s.editsRemaining-1,str:d+s.str.slice(2)})}}}return r},t.TokenSet.fromString=function(e){for(var n=new t.TokenSet,r=n,i=0,s=e.length;i=e;n--){var r=this.uncheckedNodes[n],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r.char]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}};t.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},t.Index.prototype.search=function(e){return this.query(function(n){var r=new t.QueryParser(e,n);r.parse()})},t.Index.prototype.query=function(e){for(var n=new t.Query(this.fields),r=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),l=0;l1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,n){var r=e[this._ref],i=Object.keys(this._fields);this._documents[r]=n||{},this.documentCount+=1;for(var s=0;s=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,n;do e=this.next(),n=e.charCodeAt(0);while(n>47&&n<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var n=e.next();if(n==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(n.charCodeAt(0)==92){e.escapeCharacter();continue}if(n==":")return t.QueryLexer.lexField;if(n=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(n=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(n=="+"&&e.width()===1||n=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(n.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,n){this.lexer=new t.QueryLexer(e),this.query=n,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var n=e.peekLexeme();if(n!=null)switch(n.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expected either a field or a term, found "+n.type;throw n.str.length>=1&&(r+=" with value '"+n.str+"'"),new t.QueryParseError(r,n.start,n.end)}},t.QueryParser.parsePresence=function(e){var n=e.consumeLexeme();if(n!=null){switch(n.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var r="unrecognised presence operator'"+n.str+"'";throw new t.QueryParseError(r,n.start,n.end)}var i=e.peekLexeme();if(i==null){var r="expecting term or field, found nothing";throw new t.QueryParseError(r,n.start,n.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(r,i.start,i.end)}}},t.QueryParser.parseField=function(e){var n=e.consumeLexeme();if(n!=null){if(e.query.allFields.indexOf(n.str)==-1){var r=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+n.str+"', possible fields: "+r;throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.fields=[n.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,n.start,n.end)}switch(s.type){case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseTerm=function(e){var n=e.consumeLexeme();if(n!=null){e.currentClause.term=n.str.toLowerCase(),n.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var r=e.peekLexeme();if(r==null){e.nextClause();return}switch(r.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+r.type+"'";throw new t.QueryParseError(i,r.start,r.end)}}},t.QueryParser.parseEditDistance=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="edit distance must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.editDistance=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="boost must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.boost=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},function(e,n){typeof define=="function"&&define.amd?define(n):typeof ae=="object"?le.exports=n():e.lunr=n()}(this,function(){return t})})()});var se=[];function G(t,e){se.push({selector:e,constructor:t})}var U=class{constructor(){this.alwaysVisibleMember=null;this.createComponents(document.body),this.ensureFocusedElementVisible(),this.listenForCodeCopies(),window.addEventListener("hashchange",()=>this.ensureFocusedElementVisible()),document.body.style.display||(this.ensureFocusedElementVisible(),this.updateIndexVisibility(),this.scrollToHash())}createComponents(e){se.forEach(n=>{e.querySelectorAll(n.selector).forEach(r=>{r.dataset.hasInstance||(new n.constructor({el:r,app:this}),r.dataset.hasInstance=String(!0))})})}filterChanged(){this.ensureFocusedElementVisible()}showPage(){document.body.style.display&&(document.body.style.removeProperty("display"),this.ensureFocusedElementVisible(),this.updateIndexVisibility(),this.scrollToHash())}scrollToHash(){if(location.hash){let e=document.getElementById(location.hash.substring(1));if(!e)return;e.scrollIntoView({behavior:"instant",block:"start"})}}ensureActivePageVisible(){let e=document.querySelector(".tsd-navigation .current"),n=e?.parentElement;for(;n&&!n.classList.contains(".tsd-navigation");)n instanceof HTMLDetailsElement&&(n.open=!0),n=n.parentElement;if(e&&!Ve(e)){let r=e.getBoundingClientRect().top-document.documentElement.clientHeight/4;document.querySelector(".site-menu").scrollTop=r,document.querySelector(".col-sidebar").scrollTop=r}}updateIndexVisibility(){let e=document.querySelector(".tsd-index-content"),n=e?.open;e&&(e.open=!0),document.querySelectorAll(".tsd-index-section").forEach(r=>{r.style.display="block";let i=Array.from(r.querySelectorAll(".tsd-index-link")).every(s=>s.offsetParent==null);r.style.display=i?"none":"block"}),e&&(e.open=n)}ensureFocusedElementVisible(){if(this.alwaysVisibleMember&&(this.alwaysVisibleMember.classList.remove("always-visible"),this.alwaysVisibleMember.firstElementChild.remove(),this.alwaysVisibleMember=null),!location.hash)return;let e=document.getElementById(location.hash.substring(1));if(!e)return;let n=e.parentElement;for(;n&&n.tagName!=="SECTION";)n=n.parentElement;if(!n)return;let r=n.offsetParent==null,i=n;for(;i!==document.body;)i instanceof HTMLDetailsElement&&(i.open=!0),i=i.parentElement;if(n.offsetParent==null){this.alwaysVisibleMember=n,n.classList.add("always-visible");let s=document.createElement("p");s.classList.add("warning"),s.textContent=window.translations.normally_hidden,n.prepend(s)}r&&e.scrollIntoView()}listenForCodeCopies(){document.querySelectorAll("pre > button").forEach(e=>{let n;e.addEventListener("click",()=>{e.previousElementSibling instanceof HTMLElement&&navigator.clipboard.writeText(e.previousElementSibling.innerText.trim()),e.textContent=window.translations.copied,e.classList.add("visible"),clearTimeout(n),n=setTimeout(()=>{e.classList.remove("visible"),n=setTimeout(()=>{e.textContent=window.translations.copy},100)},1e3)})})}};function Ve(t){let e=t.getBoundingClientRect(),n=Math.max(document.documentElement.clientHeight,window.innerHeight);return!(e.bottom<0||e.top-n>=0)}var oe=(t,e=100)=>{let n;return()=>{clearTimeout(n),n=setTimeout(()=>t(),e)}};var pe=Ae(ue());async function ce(t,e){if(!window.searchData)return;let n=await fetch(window.searchData),r=new Blob([await n.arrayBuffer()]).stream().pipeThrough(new DecompressionStream("gzip")),i=await new Response(r).json();t.data=i,t.index=pe.Index.load(i.index),e.classList.remove("loading"),e.classList.add("ready")}function fe(){let t=document.getElementById("tsd-search");if(!t)return;let e={base:t.dataset.base+"/"},n=document.getElementById("tsd-search-script");t.classList.add("loading"),n&&(n.addEventListener("error",()=>{t.classList.remove("loading"),t.classList.add("failure")}),n.addEventListener("load",()=>{ce(e,t)}),ce(e,t));let r=document.querySelector("#tsd-search input"),i=document.querySelector("#tsd-search .results");if(!r||!i)throw new Error("The input field or the result list wrapper was not found");i.addEventListener("mouseup",()=>{te(t)}),r.addEventListener("focus",()=>t.classList.add("has-focus")),He(t,i,r,e)}function He(t,e,n,r){n.addEventListener("input",oe(()=>{Ne(t,e,n,r)},200)),n.addEventListener("keydown",i=>{i.key=="Enter"?Be(e,t):i.key=="ArrowUp"?(de(e,n,-1),i.preventDefault()):i.key==="ArrowDown"&&(de(e,n,1),i.preventDefault())}),document.body.addEventListener("keypress",i=>{i.altKey||i.ctrlKey||i.metaKey||!n.matches(":focus")&&i.key==="/"&&(i.preventDefault(),n.focus())}),document.body.addEventListener("keyup",i=>{t.classList.contains("has-focus")&&(i.key==="Escape"||!e.matches(":focus-within")&&!n.matches(":focus"))&&(n.blur(),te(t))})}function te(t){t.classList.remove("has-focus")}function Ne(t,e,n,r){if(!r.index||!r.data)return;e.textContent="";let i=n.value.trim(),s;if(i){let o=i.split(" ").map(a=>a.length?`*${a}*`:"").join(" ");s=r.index.search(o)}else s=[];for(let o=0;oa.score-o.score);for(let o=0,a=Math.min(10,s.length);o`,d=he(l.name,i);globalThis.DEBUG_SEARCH_WEIGHTS&&(d+=` (score: ${s[o].score.toFixed(2)})`),l.parent&&(d=` ${he(l.parent,i)}.${d}`);let m=document.createElement("li");m.classList.value=l.classes??"";let p=document.createElement("a");p.href=r.base+l.url,p.innerHTML=u+d,m.append(p),p.addEventListener("focus",()=>{e.querySelector(".current")?.classList.remove("current"),m.classList.add("current")}),e.appendChild(m)}}function de(t,e,n){let r=t.querySelector(".current");if(!r)r=t.querySelector(n==1?"li:first-child":"li:last-child"),r&&r.classList.add("current");else{let i=r;if(n===1)do i=i.nextElementSibling??void 0;while(i instanceof HTMLElement&&i.offsetParent==null);else do i=i.previousElementSibling??void 0;while(i instanceof HTMLElement&&i.offsetParent==null);i?(r.classList.remove("current"),i.classList.add("current")):n===-1&&(r.classList.remove("current"),e.focus())}}function Be(t,e){let n=t.querySelector(".current");if(n||(n=t.querySelector("li:first-child")),n){let r=n.querySelector("a");r&&(window.location.href=r.href),te(e)}}function he(t,e){if(e==="")return t;let n=t.toLocaleLowerCase(),r=e.toLocaleLowerCase(),i=[],s=0,o=n.indexOf(r);for(;o!=-1;)i.push(ee(t.substring(s,o)),`${ee(t.substring(o,o+r.length))}`),s=o+r.length,o=n.indexOf(r,s);return i.push(ee(t.substring(s))),i.join("")}var je={"&":"&","<":"<",">":">","'":"'",'"':"""};function ee(t){return t.replace(/[&<>"'"]/g,e=>je[e])}var I=class{constructor(e){this.el=e.el,this.app=e.app}};var F="mousedown",ye="mousemove",N="mouseup",J={x:0,y:0},me=!1,ne=!1,qe=!1,D=!1,ve=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);document.documentElement.classList.add(ve?"is-mobile":"not-mobile");ve&&"ontouchstart"in document.documentElement&&(qe=!0,F="touchstart",ye="touchmove",N="touchend");document.addEventListener(F,t=>{ne=!0,D=!1;let e=F=="touchstart"?t.targetTouches[0]:t;J.y=e.pageY||0,J.x=e.pageX||0});document.addEventListener(ye,t=>{if(ne&&!D){let e=F=="touchstart"?t.targetTouches[0]:t,n=J.x-(e.pageX||0),r=J.y-(e.pageY||0);D=Math.sqrt(n*n+r*r)>10}});document.addEventListener(N,()=>{ne=!1});document.addEventListener("click",t=>{me&&(t.preventDefault(),t.stopImmediatePropagation(),me=!1)});var X=class extends I{constructor(e){super(e),this.className=this.el.dataset.toggle||"",this.el.addEventListener(N,n=>this.onPointerUp(n)),this.el.addEventListener("click",n=>n.preventDefault()),document.addEventListener(F,n=>this.onDocumentPointerDown(n)),document.addEventListener(N,n=>this.onDocumentPointerUp(n))}setActive(e){if(this.active==e)return;this.active=e,document.documentElement.classList.toggle("has-"+this.className,e),this.el.classList.toggle("active",e);let n=(this.active?"to-has-":"from-has-")+this.className;document.documentElement.classList.add(n),setTimeout(()=>document.documentElement.classList.remove(n),500)}onPointerUp(e){D||(this.setActive(!0),e.preventDefault())}onDocumentPointerDown(e){if(this.active){if(e.target.closest(".col-sidebar, .tsd-filter-group"))return;this.setActive(!1)}}onDocumentPointerUp(e){if(!D&&this.active&&e.target.closest(".col-sidebar")){let n=e.target.closest("a");if(n){let r=window.location.href;r.indexOf("#")!=-1&&(r=r.substring(0,r.indexOf("#"))),n.href.substring(0,r.length)==r&&setTimeout(()=>this.setActive(!1),250)}}}};var re;try{re=localStorage}catch{re={getItem(){return null},setItem(){}}}var Q=re;var ge=document.head.appendChild(document.createElement("style"));ge.dataset.for="filters";var Y=class extends I{constructor(e){super(e),this.key=`filter-${this.el.name}`,this.value=this.el.checked,this.el.addEventListener("change",()=>{this.setLocalStorage(this.el.checked)}),this.setLocalStorage(this.fromLocalStorage()),ge.innerHTML+=`html:not(.${this.key}) .tsd-is-${this.el.name} { display: none; } -`,this.app.updateIndexVisibility()}fromLocalStorage(){let e=Q.getItem(this.key);return e?e==="true":this.el.checked}setLocalStorage(e){Q.setItem(this.key,e.toString()),this.value=e,this.handleValueChange()}handleValueChange(){this.el.checked=this.value,document.documentElement.classList.toggle(this.key,this.value),this.app.filterChanged(),this.app.updateIndexVisibility()}};var Z=class extends I{constructor(e){super(e),this.summary=this.el.querySelector(".tsd-accordion-summary"),this.icon=this.summary.querySelector("svg"),this.key=`tsd-accordion-${this.summary.dataset.key??this.summary.textContent.trim().replace(/\s+/g,"-").toLowerCase()}`;let n=Q.getItem(this.key);this.el.open=n?n==="true":this.el.open,this.el.addEventListener("toggle",()=>this.update());let r=this.summary.querySelector("a");r&&r.addEventListener("click",()=>{location.assign(r.href)}),this.update()}update(){this.icon.style.transform=`rotate(${this.el.open?0:-90}deg)`,Q.setItem(this.key,this.el.open.toString())}};function Ee(t){let e=Q.getItem("tsd-theme")||"os";t.value=e,xe(e),t.addEventListener("change",()=>{Q.setItem("tsd-theme",t.value),xe(t.value)})}function xe(t){document.documentElement.dataset.theme=t}var K;function we(){let t=document.getElementById("tsd-nav-script");t&&(t.addEventListener("load",Le),Le())}async function Le(){let t=document.getElementById("tsd-nav-container");if(!t||!window.navigationData)return;let n=await(await fetch(window.navigationData)).arrayBuffer(),r=new Blob([n]).stream().pipeThrough(new DecompressionStream("gzip")),i=await new Response(r).json();K=t.dataset.base,K.endsWith("/")||(K+="/"),t.innerHTML="";for(let s of i)Se(s,t,[]);window.app.createComponents(t),window.app.showPage(),window.app.ensureActivePageVisible()}function Se(t,e,n){let r=e.appendChild(document.createElement("li"));if(t.children){let i=[...n,t.text],s=r.appendChild(document.createElement("details"));s.className=t.class?`${t.class} tsd-accordion`:"tsd-accordion";let o=s.appendChild(document.createElement("summary"));o.className="tsd-accordion-summary",o.dataset.key=i.join("$"),o.innerHTML='',be(t,o);let a=s.appendChild(document.createElement("div"));a.className="tsd-accordion-details";let l=a.appendChild(document.createElement("ul"));l.className="tsd-nested-navigation";for(let u of t.children)Se(u,l,i)}else be(t,r,t.class)}function be(t,e,n){if(t.path){let r=e.appendChild(document.createElement("a"));r.href=K+t.path,n&&(r.className=n),location.pathname===r.pathname&&r.classList.add("current"),t.kind&&(r.innerHTML=``),r.appendChild(document.createElement("span")).textContent=t.text}else e.appendChild(document.createElement("span")).textContent=t.text}G(X,"a[data-toggle]");G(Z,".tsd-accordion");G(Y,".tsd-filter-item input[type=checkbox]");var Te=document.getElementById("tsd-theme");Te&&Ee(Te);var $e=new U;Object.defineProperty(window,"app",{value:$e});fe();we();})(); +`,this.app.updateIndexVisibility()}fromLocalStorage(){let e=Q.getItem(this.key);return e?e==="true":this.el.checked}setLocalStorage(e){Q.setItem(this.key,e.toString()),this.value=e,this.handleValueChange()}handleValueChange(){this.el.checked=this.value,document.documentElement.classList.toggle(this.key,this.value),this.app.filterChanged(),this.app.updateIndexVisibility()}};var Z=class extends I{constructor(e){super(e),this.summary=this.el.querySelector(".tsd-accordion-summary"),this.icon=this.summary.querySelector("svg"),this.key=`tsd-accordion-${this.summary.dataset.key??this.summary.textContent.trim().replace(/\s+/g,"-").toLowerCase()}`;let n=Q.getItem(this.key);this.el.open=n?n==="true":this.el.open,this.el.addEventListener("toggle",()=>this.update());let r=this.summary.querySelector("a");r&&r.addEventListener("click",()=>{location.assign(r.href)}),this.update()}update(){this.icon.style.transform=`rotate(${this.el.open?0:-90}deg)`,Q.setItem(this.key,this.el.open.toString())}};function Ee(t){let e=Q.getItem("tsd-theme")||"os";t.value=e,xe(e),t.addEventListener("change",()=>{Q.setItem("tsd-theme",t.value),xe(t.value)})}function xe(t){document.documentElement.dataset.theme=t}var K;function we(){let t=document.getElementById("tsd-nav-script");t&&(t.addEventListener("load",Le),Le())}async function Le(){let t=document.getElementById("tsd-nav-container");if(!t||!window.navigationData)return;let n=await(await fetch(window.navigationData)).arrayBuffer(),r=new Blob([n]).stream().pipeThrough(new DecompressionStream("gzip")),i=await new Response(r).json();K=t.dataset.base,K.endsWith("/")||(K+="/"),t.innerHTML="";for(let s of i)Se(s,t,[]);window.app.createComponents(t),window.app.showPage(),window.app.ensureActivePageVisible()}function Se(t,e,n){let r=e.appendChild(document.createElement("li"));if(t.children){let i=[...n,t.text],s=r.appendChild(document.createElement("details"));s.className=t.class?`${t.class} tsd-accordion`:"tsd-accordion";let o=s.appendChild(document.createElement("summary"));o.className="tsd-accordion-summary",o.dataset.key=i.join("$"),o.innerHTML='',be(t,o);let a=s.appendChild(document.createElement("div"));a.className="tsd-accordion-details";let l=a.appendChild(document.createElement("ul"));l.className="tsd-nested-navigation";for(let u of t.children)Se(u,l,i)}else be(t,r,t.class)}function be(t,e,n){if(t.path){let r=e.appendChild(document.createElement("a"));r.href=K+t.path,n&&(r.className=n),location.pathname===r.pathname&&!r.href.includes("#")&&r.classList.add("current"),t.kind&&(r.innerHTML=``),r.appendChild(document.createElement("span")).textContent=t.text}else e.appendChild(document.createElement("span")).textContent=t.text}G(X,"a[data-toggle]");G(Z,".tsd-accordion");G(Y,".tsd-filter-item input[type=checkbox]");var Te=document.getElementById("tsd-theme");Te&&Ee(Te);var $e=new U;Object.defineProperty(window,"app",{value:$e});fe();we();})(); /*! Bundled license information: lunr/lunr.js: diff --git a/docs/functions/default.html b/docs/functions/default.html index 526bdfec..27c0ce43 100644 --- a/docs/functions/default.html +++ b/docs/functions/default.html @@ -1 +1 @@ -default | @switchbot/homebridge-switchbot
  • Parameters

    • api: API

    Returns void

+default | @switchbot/homebridge-switchbot
  • Parameters

    • api: API

    Returns void

diff --git a/docs/index.html b/docs/index.html index ab07ccff..392b2f21 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,17 +1,17 @@ @switchbot/homebridge-switchbot

@switchbot/homebridge-switchbot

homebridge-verified

-

@switchbot/homebridge-switchbot

npm version -npm downloads -discord-switchbot

+

@switchbot/homebridge-switchbot

npm version +npm downloads +discord-switchbot

The Homebridge SwitchBot plugin allows you to access your SwitchBot Device(s) from HomeKit with - Homebridge. + Homebridge.

    -
  1. Search for "SwitchBot" on the plugin screen of Homebridge Config UI X
  2. +
  3. Search for "SwitchBot" on the plugin screen of Homebridge Config UI X
  4. Find: @switchbot/homebridge-switchbot
      -
    • See noble prerequisites for your OS. (This is used for BLE connection.)
    • +
    • See noble prerequisites for your OS. (This is used for BLE connection.)
  5. Click Install
  6. @@ -80,10 +80,21 @@

    This lists all discovered Bluetooth devices. The BLE address of the SwitchBot device should be included in this list, otherwise your computer does not discover it.

+ +
  • +
      +
    1. Manually grant Bluetooth access in System Settings UI for Security & Privacy -> Privacy to the node executable, eg /usr/local/bin/node +Security & Privacy -> Privacy +(This is what is intended in documentation for the noble bluetooth package prerequisites by "Add terminal app", however for HomeBridge it is node that needs the permission granted, not terminal. +Without this step, then you will receive the following error when the swichbot plugin launches, which will cause Homebridge or the child bridge process to restart:
    2. +
    +
    Error: Failed to initialize the Noble object: unauthorized
    at Noble.<anonymous> (file:///usr/local/lib/node_modules/@switchbot/homebridge-switchbot/node_modules/node-switchbot/src/switchbot.ts:244:19)
    at Object.onceWrapper (node:events:629:26)
    at Noble.emit (node:events:514:28)
    at Noble.onStateChange (/usr/local/lib/node_modules/@switchbot/homebridge-switchbot/node_modules/@stoprocent/noble/lib/noble.js:92:8)
    at NobleMac.emit (node:events:514:28) +
    +
  • +
    diff --git a/docs/media/security-privacy-bluetooth.png b/docs/media/security-privacy-bluetooth.png new file mode 100644 index 00000000..7aceaf0e Binary files /dev/null and b/docs/media/security-privacy-bluetooth.png differ diff --git a/nodemon.json b/nodemon.json index 420f01fc..d2b8950f 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,5 +1,5 @@ { - "watch": ["src", "config.schema.json"], + "watch": ["src"], "ext": "ts, html, json", "ignore": [], "exec": "DEBUG= tsc && homebridge -T -D -P -I -U ~/.homebridge-dev ..", @@ -7,4 +7,4 @@ "env": { "NODE_OPTIONS": "--trace-warnings" } -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index e3835c82..44a92f38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@switchbot/homebridge-switchbot", - "version": "3.8.3", + "version": "4.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@switchbot/homebridge-switchbot", - "version": "3.8.3", + "version": "4.0.0", "funding": [ { "type": "Paypal", @@ -23,9 +23,8 @@ "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.5", "homebridge-lib": "^7.0.9", - "node-switchbot": "2.4.0", - "rxjs": "^7.8.1", - "undici": "^6.19.8" + "node-switchbot": "^3.1.0", + "rxjs": "^7.8.1" }, "devDependencies": { "@antfu/eslint-config": "^3.7.3", @@ -40,19 +39,18 @@ "eslint": "^9.12.0", "eslint-plugin-format": "^0.1.2", "homebridge": "^1.8.4", - "homebridge-config-ui-x": "4.60.1", + "homebridge-config-ui-x": "4.61.0", "nodemon": "^3.1.7", "npm-check-updates": "^17.1.3", "shx": "^0.3.4", "ts-node": "^10.9.2", - "typedoc": "^0.26.8", + "typedoc": "^0.26.9", "typescript": "^5.6.3", - "typescript-axios-wb": "^1.0.3", "vitest": "^2.1.2" }, "engines": { "homebridge": "^1.8.4 || ^2.0.0 || ^2.0.0-beta.11 || ^2.0.0-alpha.10", - "node": "^18 || ^20 || ^22" + "node": "^20 || ^22" } }, "node_modules/@ampproject/remapping": { @@ -329,13 +327,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", - "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", + "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.25.7" + "@babel/types": "^7.25.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -345,9 +343,9 @@ } }, "node_modules/@babel/types": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", - "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", + "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", "dev": true, "license": "MIT", "dependencies": { @@ -2531,7 +2529,6 @@ "integrity": "sha512-e4uG5oQMFDz+94hBtupWvnr7QvmFcNPXZXYB5DS4vqo6/2e6dvReEscRyofv/WxB14O8vc3RQC+QwHDN++vCFw==", "hasInstallScript": true, "license": "MIT", - "optional": true, "dependencies": { "debug": "^4.3.7", "napi-thread-safe-callback": "^0.0.6", @@ -3143,45 +3140,45 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.11", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.11.tgz", - "integrity": "sha512-PwAdxs7/9Hc3ieBO12tXzmTD+Ln4qhT/56S+8DvrrZ4kLDn4Z/AMUr8tXJD0axiJBS0RKIoNaR0yMuQB9v9Udg==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.12.tgz", + "integrity": "sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "@babel/parser": "^7.25.3", - "@vue/shared": "3.5.11", + "@vue/shared": "3.5.12", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.11", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.11.tgz", - "integrity": "sha512-pyGf8zdbDDRkBrEzf8p7BQlMKNNF5Fk/Cf/fQ6PiUz9at4OaUfyXW0dGJTo2Vl1f5U9jSLCNf0EZJEogLXoeew==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.12.tgz", + "integrity": "sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@vue/compiler-core": "3.5.11", - "@vue/shared": "3.5.11" + "@vue/compiler-core": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.11", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.11.tgz", - "integrity": "sha512-gsbBtT4N9ANXXepprle+X9YLg2htQk1sqH/qGJ/EApl+dgpUBdTv3yP7YlR535uHZY3n6XaR0/bKo0BgwwDniw==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.12.tgz", + "integrity": "sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "@babel/parser": "^7.25.3", - "@vue/compiler-core": "3.5.11", - "@vue/compiler-dom": "3.5.11", - "@vue/compiler-ssr": "3.5.11", - "@vue/shared": "3.5.11", + "@vue/compiler-core": "3.5.12", + "@vue/compiler-dom": "3.5.12", + "@vue/compiler-ssr": "3.5.12", + "@vue/shared": "3.5.12", "estree-walker": "^2.0.2", "magic-string": "^0.30.11", "postcss": "^8.4.47", @@ -3189,21 +3186,21 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.11", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.11.tgz", - "integrity": "sha512-P4+GPjOuC2aFTk1Z4WANvEhyOykcvEd5bIj2KVNGKGfM745LaXGr++5njpdBTzVz5pZifdlR1kpYSJJpIlSePA==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.12.tgz", + "integrity": "sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@vue/compiler-dom": "3.5.11", - "@vue/shared": "3.5.11" + "@vue/compiler-dom": "3.5.12", + "@vue/shared": "3.5.12" } }, "node_modules/@vue/shared": { - "version": "3.5.11", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.11.tgz", - "integrity": "sha512-W8GgysJVnFo81FthhzurdRAWP/byq3q2qIw70e0JWblzVhjgOMiC2GyovXrZTFQJnFVryYaKGP3Tc9vYzYm6PQ==", + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.12.tgz", + "integrity": "sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==", "dev": true, "license": "MIT", "peer": true @@ -3491,6 +3488,15 @@ "mqtt": "^4.3.7" } }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -3821,9 +3827,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001667", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz", - "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==", + "version": "1.0.30001668", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001668.tgz", + "integrity": "sha512-nWLrdxqCdblixUO+27JtGJJE/txpJlyUy5YN1u53wLZkP0emYCo5zgS6QYft7VUYR42LGgi/S5hdLZTrnyIddw==", "dev": true, "funding": [ { @@ -4223,15 +4229,6 @@ "minimist": "^1.1.0" } }, - "node_modules/commist/node_modules/leven": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", - "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4731,9 +4728,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.33", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.33.tgz", - "integrity": "sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA==", + "version": "1.5.36", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.36.tgz", + "integrity": "sha512-HYTX8tKge/VNp6FGO+f/uVDmUkq+cEfcxYhKf15Akc4M5yxt5YmorwlAitKWjWhWQnKcDRBAQKXkhqqXMqcrjw==", "dev": true, "license": "ISC" }, @@ -5238,9 +5235,9 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "50.3.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.3.1.tgz", - "integrity": "sha512-SY9oUuTMr6aWoJggUS40LtMjsRzJPB5ZT7F432xZIHK3EfHF+8i48GbUBpwanrtlL9l1gILNTHK9o8gEhYLcKA==", + "version": "50.3.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.3.2.tgz", + "integrity": "sha512-TjgZocG53N3a84PdCFGqVMWLWwDitOUuKjlJftwTu/iTiD7N/Q2Q3eEy/Q4GfJqpM4rTJCkzUYWQfol6RZNDcA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5333,9 +5330,9 @@ } }, "node_modules/eslint-plugin-n": { - "version": "17.10.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.10.3.tgz", - "integrity": "sha512-ySZBfKe49nQZWR1yFaA0v/GsH6Fgp8ah6XV0WDz6CN8WO0ek4McMzb7A2xnf4DCYV43frjCygvb9f/wx7UUxRw==", + "version": "17.11.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.11.1.tgz", + "integrity": "sha512-93IUD82N6tIEgjztVI/l3ElHtC2wTa9boJHrD8iN+NyDxjxz/daZUZKfkedjBZNdg6EqDk4irybUsiPwDqXAEA==", "dev": true, "license": "MIT", "dependencies": { @@ -5500,9 +5497,9 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.28.0.tgz", - "integrity": "sha512-ShrihdjIhOTxs+MfWun6oJWuk+g/LAhN+CiuOl/jjkG3l0F2AuK5NMTaWqyvBgkFtpYmyks6P4603mLmhNJW8g==", + "version": "9.29.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.29.0.tgz", + "integrity": "sha512-hamyjrBhNH6Li6R1h1VF9KHfshJlKgKEg3ARbGTn72CMNDSMhWbgC7NdkRDEh25AFW+4SDATzyNM+3gWuZii8g==", "dev": true, "license": "MIT", "dependencies": { @@ -6200,9 +6197,9 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dev": true, "license": "MIT", "dependencies": { @@ -6279,13 +6276,6 @@ "node": ">=8" } }, - "node_modules/fs-minipass/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6448,9 +6438,9 @@ } }, "node_modules/globals": { - "version": "15.10.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.10.0.tgz", - "integrity": "sha512-tqFIbz83w4Y5TCbtgjZjApohbuh7K9BxGYFm7ifwDR240tvdb7P9x+/9VvUKlmkPoiknoJtanI8UOrqxS3a7lQ==", + "version": "15.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.11.0.tgz", + "integrity": "sha512-yeyNSjdbyVaWurlwCpcA6XNBrHTMIeDdj0/hnvX/OLJ9ekOXYbLsLinH/MucQyGvNnXhidTdNhTtJaffL2sMfw==", "dev": true, "license": "MIT", "engines": { @@ -6461,9 +6451,9 @@ } }, "node_modules/google-auth-library": { - "version": "9.14.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.14.1.tgz", - "integrity": "sha512-Rj+PMjoNFGFTmtItH7gHfbHpGVSb3vmnGK3nwNBqxQF9NoBpttSZI/rc0WiM63ma2uGDQtYEkMHkK9U6937NiA==", + "version": "9.14.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.14.2.tgz", + "integrity": "sha512-R+FRIfk1GBo3RdlRYWPdwk8nmtVUOn6+BkDomAC46KoU8kzXzE1HLmOasSCbWUByMMAGkknVF0G5kQ69Vj7dlA==", "license": "Apache-2.0", "dependencies": { "base64-js": "^1.3.0", @@ -6819,9 +6809,9 @@ } }, "node_modules/homebridge-config-ui-x": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/homebridge-config-ui-x/-/homebridge-config-ui-x-4.60.1.tgz", - "integrity": "sha512-OOf1IQB6dcPjpqWb3FOJIYXaT7SxO359WFeTCbc82aZtMIsiOvMyKiH0Dkydq50phOSkz+T49HfL16HcXCFt4g==", + "version": "4.61.0", + "resolved": "https://registry.npmjs.org/homebridge-config-ui-x/-/homebridge-config-ui-x-4.61.0.tgz", + "integrity": "sha512-jmilxafuUxwA7Wm1jJ02zAjNQb1anz2668p63ZpBHahbHYg8kT1sIKLIb53FmlTzVrf+CCoJzXo4EJ2UUqNn0Q==", "dev": true, "funding": [ { @@ -7880,6 +7870,15 @@ "graceful-fs": "^4.1.11" } }, + "node_modules/leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -8075,6 +8074,18 @@ "dev": true, "license": "MIT" }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", @@ -8093,9 +8104,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, "license": "MIT", "dependencies": { @@ -9173,13 +9184,6 @@ "node": ">=8" } }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -9267,24 +9271,6 @@ "process-nextick-args": "^2.0.1" } }, - "node_modules/mqtt/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mqtt/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -9311,9 +9297,9 @@ "license": "MIT" }, "node_modules/nan": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", - "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==", + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", + "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", "dev": true, "license": "MIT" }, @@ -9347,8 +9333,7 @@ "version": "0.0.6", "resolved": "https://registry.npmjs.org/napi-thread-safe-callback/-/napi-thread-safe-callback-0.0.6.tgz", "integrity": "sha512-X7uHCOCdY4u0yamDxDrv3jF2NtYc8A1nvPzBQgvpoSX+WB3jAe2cVNsY448V1ucq7Whf9Wdy02HEUoLW5rJKWg==", - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -9388,11 +9373,10 @@ } }, "node_modules/node-addon-api": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.1.0.tgz", - "integrity": "sha512-yBY+qqWSv3dWKGODD6OGE6GnTX7Q2r+4+DfpqxHSHh8x0B4EKP9+wVGLS6U/AM1vxSNNmUEuIV5EGhYwPpfOwQ==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.2.1.tgz", + "integrity": "sha512-vmEOvxwiH8tlOcv4SyE8RH34rI5/nWVaigUeAUPawC6f0+HoDthwI0vkMu4tbtsZrXq6QXFfrkhjofzKEs5tpA==", "license": "MIT", - "optional": true, "engines": { "node": "^18 || ^20 || >= 21" } @@ -9435,7 +9419,6 @@ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", "license": "MIT", - "optional": true, "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -9483,13 +9466,20 @@ } }, "node_modules/node-switchbot": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.4.0.tgz", - "integrity": "sha512-Szhia/QFoHfPluB93xEDRB2hwk5WtXWXZCoUgLYXH2QaZ1m0B/kDPq8yeOfB6PjtlMd7daIZFspfaZjftT5LVw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-3.1.0.tgz", + "integrity": "sha512-Y2H0f5N0YzVFJksBaUivf6VSZm1ALgOzTJ2O4Iqu11NBDfSvUyxH9B2iLvlwqUj+R1yS66m5wqK0Esysr1JM6A==", "license": "MIT", + "dependencies": { + "@stoprocent/noble": "^1.15.1", + "async-mutex": "^0.5.0", + "undici": "^6.20.0" + }, + "engines": { + "node": "^20 || ^22" + }, "optionalDependencies": { - "@stoprocent/bluetooth-hci-socket": "^1.2.1", - "@stoprocent/noble": "^1.14.1" + "@stoprocent/bluetooth-hci-socket": "^1.4.1" } }, "node_modules/nodemon": { @@ -9923,9 +9913,9 @@ "license": "BlueOak-1.0.0" }, "node_modules/package-manager-detector": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.1.tgz", - "integrity": "sha512-/hVW2fZvAdEas+wyKh0SnlZ2mx0NIa1+j11YaQkogEJkcMErbwchHCuo8z7lEtajZJQZ6rgZNVTWMVVd71Bjng==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.2.tgz", + "integrity": "sha512-VgXbyrSNsml4eHWIvxxG/nTL4wgybMTXCV2Un/+yEc3aDKKU6nQBZjbeP3Pl3qm9Qg92X/1ng4ffvCeD/zwHgg==", "dev": true, "license": "MIT" }, @@ -10087,16 +10077,6 @@ "node": ">=10" } }, - "node_modules/patch-package/node_modules/slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -10732,16 +10712,6 @@ "node": ">=8" } }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, "node_modules/read-pkg/node_modules/type-fest": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", @@ -11551,6 +11521,16 @@ "dev": true, "license": "MIT" }, + "node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/slashes": { "version": "3.0.12", "resolved": "https://registry.npmjs.org/slashes/-/slashes-3.0.12.tgz", @@ -12151,13 +12131,6 @@ "node": ">=10" } }, - "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, "node_modules/tcp-port-used": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz", @@ -12500,6 +12473,16 @@ "node": ">= 0.8.0" } }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -12507,9 +12490,9 @@ "license": "MIT" }, "node_modules/typedoc": { - "version": "0.26.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.8.tgz", - "integrity": "sha512-QBF0BMbnNeUc6U7pRHY7Jb8pjhmiNWZNQT8LU6uk9qP9t3goP9bJptdlNqMC0wBB2w9sQrxjZt835bpRSSq1LA==", + "version": "0.26.9", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.26.9.tgz", + "integrity": "sha512-Rc7QpWL7EtmrT8yxV0GmhOR6xHgFnnhphbD9Suti3fz3um7ZOrou6q/g9d6+zC5PssTLZmjaW4Upmzv8T1rCcQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -12543,16 +12526,6 @@ "node": ">=14.17" } }, - "node_modules/typescript-axios-wb": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typescript-axios-wb/-/typescript-axios-wb-1.0.3.tgz", - "integrity": "sha512-lS0aojPRHE5nx1gglcqYXfzoEQWgxmMfXbTbozivTNJM6PfCS3a5sHfQPmxy7WQS1piQdGExB3/3GdHvtMDiqw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", @@ -12588,9 +12561,9 @@ "license": "MIT" }, "node_modules/undici": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz", - "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.20.0.tgz", + "integrity": "sha512-AITZfPuxubm31Sx0vr8bteSalEbs9wQb/BOBi9FPlD9Qpd6HxZ4Q0+hI742jBhkPb4RT2v5MQzaW5VhRVyj+9A==", "license": "MIT", "engines": { "node": ">=18.17" @@ -13388,6 +13361,12 @@ "node": ">=10" } }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/yaml": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", diff --git a/package.json b/package.json index 2f9e7d7d..a49575a3 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@switchbot/homebridge-switchbot", "displayName": "SwitchBot", "type": "module", - "version": "3.8.3", + "version": "4.0.0", "description": "The SwitchBot plugin allows you to access your SwitchBot device(s) from HomeKit.", "author": "SwitchBot (https://github.com/SwitchBot)", "contributors": [ @@ -56,7 +56,7 @@ "engineStrict": true, "engines": { "homebridge": "^1.8.4 || ^2.0.0 || ^2.0.0-beta.11 || ^2.0.0-alpha.10", - "node": "^18 || ^20 || ^22" + "node": "^20 || ^22" }, "scripts": { "check": "npm install && npm outdated", @@ -67,7 +67,7 @@ "plugin-ui": "rsync ./src/homebridge-ui/public/index.html ./dist/homebridge-ui/public/", "build": "npm run clean && tsc && npm run plugin-ui", "prepublishOnly": "npm run lint && npm run build && npm run plugin-ui ", - "postpublish": "npm run clean", + "postpublish": "npm run clean && npm ci", "clean": "shx rm -rf ./dist", "test": "npm run lint", "docs": "typedoc", @@ -81,9 +81,8 @@ "async-mqtt": "^2.6.3", "fakegato-history": "^0.6.5", "homebridge-lib": "^7.0.9", - "node-switchbot": "2.4.0", - "rxjs": "^7.8.1", - "undici": "^6.19.8" + "node-switchbot": "^3.1.0", + "rxjs": "^7.8.1" }, "devDependencies": { "@antfu/eslint-config": "^3.7.3", @@ -98,14 +97,13 @@ "eslint": "^9.12.0", "eslint-plugin-format": "^0.1.2", "homebridge": "^1.8.4", - "homebridge-config-ui-x": "4.60.1", + "homebridge-config-ui-x": "4.61.0", "nodemon": "^3.1.7", "npm-check-updates": "^17.1.3", "shx": "^0.3.4", "ts-node": "^10.9.2", - "typedoc": "^0.26.8", + "typedoc": "^0.26.9", "typescript": "^5.6.3", - "typescript-axios-wb": "^1.0.3", "vitest": "^2.1.2" }, "directories": { diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts index ff05d999..caf3165e 100644 --- a/src/device/blindtilt.ts +++ b/src/device/blindtilt.ts @@ -3,13 +3,10 @@ * blindtilt.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { blindTiltServiceData, blindTiltStatus, blindTiltWebhookContext, bodyChange, device, SwitchbotDevice, WoBlindTilt } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { blindTiltServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { blindTiltStatus } from '../types/devicestatus.js' -import type { blindTiltWebhookContext } from '../types/devicewebhookstatus.js' +import type { blindTiltConfig, devicesConfig } from '../settings.js' /* * For Testing Locally: @@ -88,7 +85,8 @@ export class BlindTilt extends deviceBase { accessory.category = this.hap.Categories.WINDOW_COVERING // default placeholders - this.mappingMode = (device.blindTilt?.mode as BlindTiltMappingMode) ?? BlindTiltMappingMode.OnlyUp + + this.mappingMode = ((device as blindTiltConfig).mapping as BlindTiltMappingMode) ?? BlindTiltMappingMode.OnlyUp this.debugLog(`Mapping mode: ${this.mappingMode}`) // this is subject we use to track when we need to POST changes to the SwitchBot API @@ -112,7 +110,7 @@ export class BlindTilt extends deviceBase { // Initialize WindowCovering Characteristics this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.Name, this.WindowCovering.Name).getCharacteristic(this.hap.Characteristic.TargetPosition).setProps({ - minStep: device.blindTilt?.set_minStep ?? 1, + minStep: (device as blindTiltConfig).set_minStep ?? 1, minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -122,7 +120,7 @@ export class BlindTilt extends deviceBase { // Initialize WindowCovering CurrentPosition Characteristic this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.CurrentPosition).setProps({ - minStep: device.blindTilt?.set_minStep ?? 1, + minStep: (device as blindTiltConfig).set_minStep ?? 1, minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -165,7 +163,7 @@ export class BlindTilt extends deviceBase { this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name).setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE) // Initialize LightSensor Service - if (device.blindTilt?.hide_lightsensor) { + if ((device as blindTiltConfig).hide_lightsensor) { if (this.LightSensor?.Service) { this.debugLog('Removing Light Sensor Service') this.LightSensor.Service = accessory.getService(this.hap.Service.LightSensor) as Service @@ -190,7 +188,7 @@ export class BlindTilt extends deviceBase { } // Initialize Open Mode Switch Service - if (!device.blindTilt?.silentModeSwitch) { + if (!(device as blindTiltConfig).silentModeSwitch) { if (this.OpenModeSwitch?.Service) { this.debugLog('Removing Open Mode Switch Service') this.OpenModeSwitch.Service = this.accessory.getService(this.hap.Service.Switch) as Service @@ -215,7 +213,7 @@ export class BlindTilt extends deviceBase { } // Initialize Close Mode Switch Service - if (!device.blindTilt?.silentModeSwitch) { + if (!(device as blindTiltConfig).silentModeSwitch) { if (this.CloseModeSwitch?.Service) { this.debugLog('Removing Close Mode Switch Service') this.CloseModeSwitch.Service = this.accessory.getService(this.hap.Service.Switch) as Service @@ -244,7 +242,7 @@ export class BlindTilt extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -252,7 +250,7 @@ export class BlindTilt extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -260,7 +258,7 @@ export class BlindTilt extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -344,9 +342,9 @@ export class BlindTilt extends deviceBase { await this.debugLog(`CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState}`) // CurrentAmbientLightLevel - if (!this.device.blindTilt?.hide_lightsensor && this.LightSensor?.Service) { - const set_minLux = this.device.blindTilt?.set_minLux ?? 1 - const set_maxLux = this.device.blindTilt?.set_maxLux ?? 6001 + if (!(this.device as blindTiltConfig).hide_lightsensor && this.LightSensor?.Service) { + const set_minLux = (this.device as blindTiltConfig).set_minLux ?? 1 + const set_maxLux = (this.device as blindTiltConfig).set_maxLux ?? 6001 const spaceBetweenLevels = 9 await this.getLightLevel(this.serviceData.lightLevel, set_minLux, set_maxLux, spaceBetweenLevels) @@ -354,14 +352,16 @@ export class BlindTilt extends deviceBase { } // BatteryLevel - this.Battery.BatteryLevel = this.serviceData.battery - await this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`) - - // StatusLowBattery - this.Battery.StatusLowBattery = this.Battery.BatteryLevel < 10 - ? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW - : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL - await this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`) + if (this.serviceData?.battery) { + this.Battery.BatteryLevel = this.serviceData.battery + await this.debugLog(`BatteryLevel: ${this.Battery.BatteryLevel}`) + + // StatusLowBattery + this.Battery.StatusLowBattery = this.Battery.BatteryLevel < 10 + ? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW + : this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL + await this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`) + } }; /** @@ -374,9 +374,9 @@ export class BlindTilt extends deviceBase { // CurrentPosition await this.getCurrentPosttionDirection(this.deviceStatus.direction, this.deviceStatus.slidePosition) - if (!this.device.blindTilt?.hide_lightsensor && this.LightSensor?.Service) { - const set_minLux = this.device.blindTilt?.set_minLux ?? 1 - const set_maxLux = this.device.blindTilt?.set_maxLux ?? 6001 + if (!(this.device as blindTiltConfig).hide_lightsensor && this.LightSensor?.Service) { + const set_minLux = (this.device as blindTiltConfig).set_minLux ?? 1 + const set_maxLux = (this.device as blindTiltConfig).set_maxLux ?? 6001 const lightLevel = this.deviceStatus.lightLevel === 'bright' ? set_maxLux : set_minLux this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 2) await this.debugLog(`LightLevel: ${this.deviceStatus.lightLevel}, CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`) @@ -457,22 +457,22 @@ export class BlindTilt extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as blindTiltServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as blindTiltServiceData // Update HomeKit if (serviceData.model === SwitchBotBLEModel.BlindTilt && serviceData.modelName === SwitchBotBLEModelName.BlindTilt) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -493,7 +493,7 @@ export class BlindTilt extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -507,17 +507,17 @@ export class BlindTilt extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -535,7 +535,7 @@ export class BlindTilt extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -567,21 +567,22 @@ export class BlindTilt extends deviceBase { await this.debugLog('BLEpushChanges') if (this.WindowCovering.TargetPosition !== this.WindowCovering.CurrentPosition) { await this.debugLog(`BLEpushChanges On: ${this.WindowCovering.TargetPosition} OnCached: ${this.WindowCovering.CurrentPosition}`) - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) const { setPositionMode, Mode }: { setPositionMode: number, Mode: string } = await this.setPerformance() await this.debugLog(`Mode: ${Mode}, setPositionMode: ${setPositionMode}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, quick: true, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: SwitchbotDevice[]) => { + const deviceList = device_list as unknown as WoBlindTilt[] return await this.retryBLE({ max: await this.maxRetryBLE(), fn: async () => { - return await device_list[0].runToPos(100 - Number(this.WindowCovering.TargetPosition), setPositionMode) + return await deviceList[0].runToPos(100 - Number(this.WindowCovering.TargetPosition), setPositionMode) }, }) }) @@ -595,7 +596,7 @@ export class BlindTilt extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -617,36 +618,35 @@ export class BlindTilt extends deviceBase { const { Mode, setPositionMode }: { setPositionMode: number, Mode: string } = await this.setPerformance() await this.debugLog(`Pushing ${this.WindowCovering.TargetPosition} (device = ${direction};${position})`) await this.debugLog(`Mode: ${Mode}, setPositionMode: ${setPositionMode}`) - let bodyChange: string + let bodyChange: bodyChange if (position === 100) { - bodyChange = JSON.stringify({ + bodyChange = { command: 'fullyOpen', parameter: 'default', commandType: 'command', - }) + } } else if (position === 0) { - bodyChange = JSON.stringify({ + bodyChange = { command: direction === 'up' ? 'closeUp' : 'closeDown', parameter: 'default', commandType: 'command', - }) + } } else { - bodyChange = JSON.stringify({ + bodyChange = { command: 'setPosition', parameter: `${direction};${position}`, commandType: 'command', - }) + } } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -729,7 +729,7 @@ export class BlindTilt extends deviceBase { * Handle requests to set the value of the "Target Position" characteristic */ async OpenModeSwitchSet(value: CharacteristicValue): Promise { - if (this.OpenModeSwitch && this.device.blindTilt?.silentModeSwitch) { + if (this.OpenModeSwitch && (this.device as blindTiltConfig).silentModeSwitch) { this.debugLog(`Silent Open Mode: ${value}`) this.OpenModeSwitch.On = value this.accessory.context.OpenModeSwitch.On = value @@ -741,7 +741,7 @@ export class BlindTilt extends deviceBase { * Handle requests to set the value of the "Target Position" characteristic */ async CloseModeSwitchSet(value: CharacteristicValue): Promise { - if (this.CloseModeSwitch && this.device.blindTilt?.silentModeSwitch) { + if (this.CloseModeSwitch && (this.device as blindTiltConfig).silentModeSwitch) { this.debugLog(`Silent Close Mode: ${value}`) this.CloseModeSwitch.On = value this.accessory.context.CloseModeSwitch.On = value @@ -762,7 +762,7 @@ export class BlindTilt extends deviceBase { // TargetPosition await this.updateCharacteristic(this.WindowCovering.Service, this.hap.Characteristic.TargetPosition, this.WindowCovering.TargetPosition, 'TargetPosition') // CurrentAmbientLightLevel - if (!this.device.blindTilt?.hide_lightsensor && this.LightSensor?.Service) { + if (!(this.device as blindTiltConfig).hide_lightsensor && this.LightSensor?.Service) { const history = { time: Math.round(new Date().valueOf() / 1000), lux: this.LightSensor.CurrentAmbientLightLevel } await this.updateCharacteristic(this.LightSensor?.Service, this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor?.CurrentAmbientLightLevel, 'CurrentAmbientLightLevel', history) } @@ -793,10 +793,10 @@ export class BlindTilt extends deviceBase { let setPositionMode: number let Mode: string if (Number(this.WindowCovering.TargetPosition) > 50) { - if (this.device.blindTilt?.setOpenMode === '1' || this.OpenModeSwitch?.On) { + if ((this.device as blindTiltConfig).setOpenMode === '1' || this.OpenModeSwitch?.On) { setPositionMode = 1 Mode = 'Silent Mode' - } else if (this.device.blindTilt?.setOpenMode === '0' || !this.OpenModeSwitch?.On) { + } else if ((this.device as blindTiltConfig).setOpenMode === '0' || !this.OpenModeSwitch?.On) { setPositionMode = 0 Mode = 'Performance Mode' } else { @@ -804,10 +804,10 @@ export class BlindTilt extends deviceBase { Mode = 'Default Mode' } } else { - if (this.device.blindTilt?.setCloseMode === '1' || this.CloseModeSwitch?.On) { + if ((this.device as blindTiltConfig).setCloseMode === '1' || this.CloseModeSwitch?.On) { setPositionMode = 1 Mode = 'Silent Mode' - } else if (this.device.blindTilt?.setOpenMode === '0' || !this.CloseModeSwitch?.On) { + } else if ((this.device as blindTiltConfig).setOpenMode === '0' || !this.CloseModeSwitch?.On) { setPositionMode = 0 Mode = 'Performance Mode' } else { @@ -819,13 +819,13 @@ export class BlindTilt extends deviceBase { } async setMinMax(): Promise { - if (this.device.blindTilt?.set_min) { - if (Number(this.WindowCovering.CurrentPosition) <= this.device.blindTilt?.set_min) { + if ((this.device as blindTiltConfig).set_min) { + if (Number(this.WindowCovering.CurrentPosition) <= (this.device as blindTiltConfig).set_min!) { this.WindowCovering.CurrentPosition = 0 } } - if (this.device.blindTilt?.set_max) { - if (Number(this.WindowCovering.CurrentPosition) >= this.device.blindTilt?.set_max) { + if ((this.device as blindTiltConfig).set_max) { + if (Number(this.WindowCovering.CurrentPosition) >= (this.device as blindTiltConfig).set_max!) { this.WindowCovering.CurrentPosition = 100 } } @@ -859,7 +859,7 @@ export class BlindTilt extends deviceBase { this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e) this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e) this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, e) - if (!this.device.blindTilt?.hide_lightsensor && this.LightSensor?.Service) { + if (!(this.device as blindTiltConfig).hide_lightsensor && this.LightSensor?.Service) { this.LightSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e) this.LightSensor.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e) } diff --git a/src/device/bot.ts b/src/device/bot.ts index 345d5595..f5a47d35 100644 --- a/src/device/bot.ts +++ b/src/device/bot.ts @@ -3,23 +3,21 @@ * bot.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, botServiceData, botStatus, botWebhookContext, device, SwitchbotDevice, WoHand } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { botServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { botStatus } from '../types/devicestatus.js' -import type { botWebhookContext } from '../types/devicewebhookstatus.js' +import type { botConfig, devicesConfig } from '../settings.js' + +import { debounceTime, interval, skipWhile, Subject, take, tap } from 'rxjs' + +import { formatDeviceIdAsMac } from '../utils.js' +import { deviceBase } from './device.js' /* * For Testing Locally: * import { SwitchBotBLEModel, SwitchBotBLEModelName } from '/Users/Shared/GitHub/OpenWonderLabs/node-switchbot/dist/index.js'; */ import { SwitchBotBLEModel, SwitchBotBLEModelName } from 'node-switchbot' -import { debounceTime, interval, skipWhile, Subject, take, tap } from 'rxjs' - -import { formatDeviceIdAsMac } from '../utils.js' -import { deviceBase } from './device.js' /** * Platform Accessory @@ -108,6 +106,18 @@ export class Bot extends deviceBase { botUpdateInProgress!: boolean doBotUpdate!: Subject + /** + * Constructs a new instance of the Bot device. + * + * @param {SwitchBotPlatform} platform - The platform instance. + * @param {PlatformAccessory} accessory - The platform accessory. + * @param {device & devicesConfig} device - The device configuration. + * + * Initializes the Bot device, sets up the battery service, maps the device type to the appropriate HomeKit service, + * removes unnecessary services, retrieves initial values, registers event handlers, and starts update intervals. + * + * @constructor + */ constructor( readonly platform: SwitchBotPlatform, accessory: PlatformAccessory, @@ -115,7 +125,6 @@ export class Bot extends deviceBase { ) { super(platform, accessory, device) - // default placeholders this.getBotConfigSettings(device) // this is subject we use to track when we need to POST changes to the SwitchBot API @@ -134,267 +143,79 @@ export class Bot extends deviceBase { // Initialize Battery Characteristics this.Battery.Service.setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name).setCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery).setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE) - // deviceType - if (this.botDeviceType === 'switch') { - // Set category - accessory.category = this.hap.Categories.SWITCH - // Initialize Switch Service - accessory.context.Switch = accessory.context.Switch ?? {} - this.Switch = { - Name: accessory.displayName, - Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch) as Service, - } - accessory.context.Switch = this.Switch as object - this.debugLog('Displaying as Switch') - // Initialize Switch Characteristics - this.Switch.Service.setCharacteristic(this.hap.Characteristic.Name, this.Switch.Name).getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)) - // Remove other services - this.removeFanService(accessory) - this.removeLockService(accessory) - this.removeDoorService(accessory) - this.removeFaucetService(accessory) - this.removeOutletService(accessory) - this.removeWindowService(accessory) - this.removeGarageDoorService(accessory) - this.removeWindowCoveringService(accessory) - this.removeStatefulProgrammableSwitchService(accessory) - } else if (this.botDeviceType === 'garagedoor') { - // Set category - accessory.category = this.hap.Categories.GARAGE_DOOR_OPENER - // Initialize GarageDoor Service - accessory.context.GarageDoor = accessory.context.GarageDoor ?? {} - this.GarageDoor = { - Name: accessory.displayName, - Service: accessory.getService(this.hap.Service.GarageDoorOpener) ?? accessory.addService(this.hap.Service.GarageDoorOpener) as Service, - } - accessory.context.GarageDoor = this.GarageDoor as object - this.debugLog('Displaying as Garage Door Opener') - // Initialize GarageDoor Characteristics - this.GarageDoor.Service.setCharacteristic(this.hap.Characteristic.Name, this.GarageDoor.Name).setCharacteristic(this.hap.Characteristic.ObstructionDetected, false).getCharacteristic(this.hap.Characteristic.TargetDoorState).setProps({ - validValues: [0, 100], - minValue: 0, - maxValue: 100, - minStep: 100, - }).onSet(this.OnSet.bind(this)) - // Remove other services - this.removeFanService(accessory) - this.removeLockService(accessory) - this.removeDoorService(accessory) - this.removeFaucetService(accessory) - this.removeOutletService(accessory) - this.removeSwitchService(accessory) - this.removeWindowService(accessory) - this.removeWindowCoveringService(accessory) - this.removeStatefulProgrammableSwitchService(accessory) - } else if (this.botDeviceType === 'door') { - // Set category - accessory.category = this.hap.Categories.DOOR - // Initialize Door Service - accessory.context.Door = accessory.context.Door ?? {} - this.Door = { - Name: accessory.displayName, - Service: accessory.getService(this.hap.Service.Door) ?? accessory.addService(this.hap.Service.Door) as Service, - } - accessory.context.Door = this.Door as object - this.debugLog('Displaying as Door') - // Initialize Door Characteristics - this.Door.Service.setCharacteristic(this.hap.Characteristic.Name, this.Door.Name).setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED).getCharacteristic(this.hap.Characteristic.TargetPosition).setProps({ - validValues: [0, 100], - minValue: 0, - maxValue: 100, - minStep: 100, - }).onSet(this.OnSet.bind(this)) - // Remove other services - this.removeFanService(accessory) - this.removeLockService(accessory) - this.removeOutletService(accessory) - this.removeFaucetService(accessory) - this.removeSwitchService(accessory) - this.removeWindowService(accessory) - this.removeGarageDoorService(accessory) - this.removeWindowCoveringService(accessory) - this.removeStatefulProgrammableSwitchService(accessory) - } else if (this.botDeviceType === 'window') { - // Set category - accessory.category = this.hap.Categories.WINDOW - // Initialize Window Service - accessory.context.Window = accessory.context.Window ?? {} - this.Window = { - Name: accessory.displayName, - Service: accessory.getService(this.hap.Service.Window) ?? accessory.addService(this.hap.Service.Window) as Service, - } - accessory.context.Window = this.Window as object - this.debugLog('Displaying as Window') - // Initialize Window Characteristics - this.Window.Service.setCharacteristic(this.hap.Characteristic.Name, this.Window.Name).setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED).getCharacteristic(this.hap.Characteristic.TargetPosition).setProps({ - validValues: [0, 100], - minValue: 0, - maxValue: 100, - minStep: 100, - }).onSet(this.OnSet.bind(this)) - // Remove other services - this.removeFanService(accessory) - this.removeLockService(accessory) - this.removeDoorService(accessory) - this.removeOutletService(accessory) - this.removeFaucetService(accessory) - this.removeSwitchService(accessory) - this.removeGarageDoorService(accessory) - this.removeWindowCoveringService(accessory) - this.removeStatefulProgrammableSwitchService(accessory) - } else if (this.botDeviceType === 'windowcovering') { + /** + * A mapping of device types to their corresponding HomeKit categories, services, and characteristics. + * + * Each key represents a device type and maps to an object containing: + * - `category`: The HomeKit category for the device. + * - `service`: The HomeKit service associated with the device. + * - `characteristic`: The HomeKit characteristic for the device. + */ + const deviceTypeMap: { [key: string]: { category: number, service: any, characteristic: any } } = { + switch: { category: this.hap.Categories.SWITCH, service: this.hap.Service.Switch, characteristic: this.hap.Characteristic.On }, + garagedoor: { category: this.hap.Categories.GARAGE_DOOR_OPENER, service: this.hap.Service.GarageDoorOpener, characteristic: this.hap.Characteristic.TargetDoorState }, + door: { category: this.hap.Categories.DOOR, service: this.hap.Service.Door, characteristic: this.hap.Characteristic.TargetPosition }, + window: { category: this.hap.Categories.WINDOW, service: this.hap.Service.Window, characteristic: this.hap.Characteristic.TargetPosition }, + windowcovering: { category: this.hap.Categories.WINDOW_COVERING, service: this.hap.Service.WindowCovering, characteristic: this.hap.Characteristic.TargetPosition }, + lock: { category: this.hap.Categories.DOOR_LOCK, service: this.hap.Service.LockMechanism, characteristic: this.hap.Characteristic.LockTargetState }, + faucet: { category: this.hap.Categories.FAUCET, service: this.hap.Service.Faucet, characteristic: this.hap.Characteristic.Active }, + fan: { category: this.hap.Categories.FAN, service: this.hap.Service.Fanv2, characteristic: this.hap.Characteristic.Active }, + stateful: { category: this.hap.Categories.PROGRAMMABLE_SWITCH, service: this.hap.Service.StatefulProgrammableSwitch, characteristic: this.hap.Characteristic.ProgrammableSwitchOutputState }, + outlet: { category: this.hap.Categories.OUTLET, service: this.hap.Service.Outlet, characteristic: this.hap.Characteristic.On }, + } + + /** + * The type of the device, determined by mapping the botDeviceType to a value in the deviceTypeMap. + */ + const deviceType = deviceTypeMap[this.botDeviceType] + + if (deviceType) { // Set category - accessory.category = this.hap.Categories.WINDOW_COVERING - // Initialize WindowCovering Service - accessory.context.WindowCovering = accessory.context.WindowCovering ?? {} - this.WindowCovering = { + accessory.category = deviceType.category + // Initialize Service + const contextKey = this.botDeviceType.charAt(0).toUpperCase() + this.botDeviceType.slice(1) + accessory.context[contextKey] = accessory.context[contextKey] ?? {} + this[this.botDeviceType.charAt(0).toUpperCase() + this.botDeviceType.slice(1)] = { Name: accessory.displayName, - Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service, + Service: accessory.getService(deviceType.service) ?? accessory.addService(deviceType.service) as Service, } - accessory.context.WindowCovering = this.WindowCovering as object - this.debugLog('Displaying as Window Covering') - // Initialize WindowCovering Characteristics - this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.Name, this.WindowCovering.Name).setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED).getCharacteristic(this.hap.Characteristic.TargetPosition).setProps({ - validValues: [0, 100], - minValue: 0, - maxValue: 100, - minStep: 100, - }).onSet(this.OnSet.bind(this)) - // Remove other services - this.removeFanService(accessory) - this.removeLockService(accessory) - this.removeDoorService(accessory) - this.removeOutletService(accessory) - this.removeFaucetService(accessory) - this.removeSwitchService(accessory) - this.removeWindowService(accessory) - this.removeGarageDoorService(accessory) - this.removeStatefulProgrammableSwitchService(accessory) - } else if (this.botDeviceType === 'lock') { - // Set category - accessory.category = this.hap.Categories.DOOR_LOCK - // Initialize Lock Service - accessory.context.LockMechanism = accessory.context.LockMechanism ?? {} - this.LockMechanism = { - Name: accessory.displayName, - Service: accessory.getService(this.hap.Service.LockMechanism) ?? accessory.addService(this.hap.Service.LockMechanism) as Service, - } - accessory.context.LockMechanism = this.LockMechanism as object - this.debugLog('Displaying as Lock') - // Initialize Lock Characteristics - this.LockMechanism.Service.setCharacteristic(this.hap.Characteristic.Name, this.LockMechanism.Name).setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED).getCharacteristic(this.hap.Characteristic.LockTargetState).onSet(this.OnSet.bind(this)) - // Remove other services - this.removeFanService(accessory) - this.removeDoorService(accessory) - this.removeOutletService(accessory) - this.removeSwitchService(accessory) - this.removeFaucetService(accessory) - this.removeWindowService(accessory) - this.removeGarageDoorService(accessory) - this.removeWindowCoveringService(accessory) - this.removeStatefulProgrammableSwitchService(accessory) - } else if (this.botDeviceType === 'faucet') { - // Set category - accessory.category = this.hap.Categories.FAUCET - // Initialize Faucet Service - accessory.context.Faucet = accessory.context.Faucet ?? {} - this.Faucet = { - Name: accessory.displayName, - Service: accessory.getService(this.hap.Service.Faucet) ?? accessory.addService(this.hap.Service.Faucet) as Service, - } - accessory.context.Faucet = this.Faucet as object - this.debugLog('Displaying as Faucet') - // Initialize Faucet Characteristics - this.Faucet.Service.setCharacteristic(this.hap.Characteristic.Name, this.Faucet.Name).getCharacteristic(this.hap.Characteristic.Active).onSet(this.OnSet.bind(this)) - // Remove other services - this.removeFanService(accessory) - this.removeLockService(accessory) - this.removeDoorService(accessory) - this.removeOutletService(accessory) - this.removeSwitchService(accessory) - this.removeWindowService(accessory) - this.removeGarageDoorService(accessory) - this.removeWindowCoveringService(accessory) - this.removeStatefulProgrammableSwitchService(accessory) - } else if (this.botDeviceType === 'fan') { - // Set category - accessory.category = this.hap.Categories.FAN - // Initialize Fan Service - accessory.context.Fan = accessory.context.Fan ?? {} - this.Fan = { - Name: accessory.displayName, - Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2) as Service, - } - accessory.context.Fan = this.Fan as object - this.debugLog('Displaying as Fan') - // Initialize Fan Characteristics - this.Fan.Service.setCharacteristic(this.hap.Characteristic.Name, this.Fan.Name).getCharacteristic(this.hap.Characteristic.Active).onSet(this.OnSet.bind(this)) - // Remove other services - this.removeLockService(accessory) - this.removeDoorService(accessory) - this.removeFaucetService(accessory) - this.removeOutletService(accessory) - this.removeSwitchService(accessory) - this.removeWindowService(accessory) - this.removeGarageDoorService(accessory) - this.removeWindowCoveringService(accessory) - this.removeStatefulProgrammableSwitchService(accessory) - } else if (this.botDeviceType === 'stateful') { - // Set category - accessory.category = this.hap.Categories.PROGRAMMABLE_SWITCH - // Initialize StatefulProgrammableSwitch Service - accessory.context.StatefulProgrammableSwitch = accessory.context.StatefulProgrammableSwitch ?? {} - this.StatefulProgrammableSwitch = { - Name: accessory.displayName, - Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) ?? accessory.addService(this.hap.Service.StatefulProgrammableSwitch) as Service, - } - accessory.context.StatefulProgrammableSwitch = this.StatefulProgrammableSwitch as object - this.debugLog('Displaying as Stateful Programmable Switch') - // Initialize StatefulProgrammableSwitch Characteristics - this.StatefulProgrammableSwitch.Service.setCharacteristic(this.hap.Characteristic.Name, this.StatefulProgrammableSwitch.Name).getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState).onSet(this.OnSet.bind(this)) - // Remove other services - this.removeFanService(accessory) - this.removeLockService(accessory) - this.removeDoorService(accessory) - this.removeFaucetService(accessory) - this.removeOutletService(accessory) - this.removeSwitchService(accessory) - this.removeWindowService(accessory) - this.removeGarageDoorService(accessory) - this.removeWindowCoveringService(accessory) - } else if (this.botDeviceType === 'outlet') { - // Set category - accessory.category = this.hap.Categories.OUTLET - // Initialize Switch property - accessory.context.Outlet = accessory.context.Outlet ?? {} - this.Outlet = { - Name: accessory.displayName, - Service: accessory.getService(this.hap.Service.Outlet) ?? accessory.addService(this.hap.Service.Outlet) as Service, - } - accessory.context.Outlet = this.Outlet as object - this.debugLog('Displaying as Outlet') - // Initialize Outlet Characteristics - this.Outlet.Service.setCharacteristic(this.hap.Characteristic.Name, this.Outlet.Name).getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this)) - // Remove other services - this.removeFanService(accessory) - this.removeLockService(accessory) - this.removeDoorService(accessory) - this.removeFaucetService(accessory) - this.removeSwitchService(accessory) - this.removeWindowService(accessory) - this.removeGarageDoorService(accessory) - this.removeWindowCoveringService(accessory) - this.removeStatefulProgrammableSwitchService(accessory) + accessory.context[contextKey] = this[this.botDeviceType.charAt(0).toUpperCase() + this.botDeviceType.slice(1)] as object + this.debugLog(`Displaying as ${contextKey}`) + // Initialize Characteristics + this[this.botDeviceType.charAt(0).toUpperCase() + this.botDeviceType.slice(1)].Service.setCharacteristic(this.hap.Characteristic.Name, this[this.botDeviceType.charAt(0).toUpperCase() + this.botDeviceType.slice(1)].Name).getCharacteristic(deviceType.characteristic).onSet(this.OnSet.bind(this)) } else { this.errorLog('Device Type not set') } + /** + * An array of service removal functions that should be executed based on the current state of the device. + * Each element in the array is a function that removes a specific service if the corresponding condition is met. + */ + const servicesToRemove = [ + !this.StatefulProgrammableSwitch && this.removeStatefulProgrammableSwitchService, + !this.Outlet && this.removeOutletService, + !this.Window && this.removeWindowService, + !this.GarageDoor && this.removeGarageDoorService, + this.WindowCovering && this.removeWindowCoveringService, + !this.Switch && this.removeSwitchService, + !this.Faucet && this.removeFaucetService, + !this.Door && this.removeDoorService, + !this.LockMechanism && this.removeLockService, + !this.Fan && this.removeFanService, + ].filter(Boolean) + + for (const removeService of servicesToRemove) { + if (typeof removeService === 'function') { + removeService.call(this, accessory) + } + } + // Retrieve initial values and updateHomekit try { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -402,7 +223,7 @@ export class Bot extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -410,7 +231,7 @@ export class Bot extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -458,7 +279,7 @@ export class Bot extends deviceBase { // BLEmode (true if Switch Mode) | (false if Press Mode) this.On = this.serviceData.mode ? this.serviceData.state : false const mode = this.serviceData.mode ? 'Switch' : 'Press' - await this.debugLog(`${mode} Mode, On: ${this.accessory.context.On}`) + await this.debugLog(`${mode} Mode, On: ${this.On}`) this.accessory.context.On = this.On // BatteryLevel @@ -481,8 +302,8 @@ export class Bot extends deviceBase { // On this.On = this.botMode === 'press' ? false : this.deviceStatus.power === 'on' + await this.debugLog(`On: ${this.On}`) this.accessory.context.On = this.On - await this.debugLog(`On: ${this.accessory.context.On}`) // Battery Level this.Battery.BatteryLevel = this.deviceStatus.battery @@ -506,7 +327,7 @@ export class Bot extends deviceBase { .getCharacteristic(this.hap.Characteristic.FirmwareRevision) .updateValue(deviceVersion) this.accessory.context.version = deviceVersion - await this.debugLog(`version: ${this.accessory.context.version}`) + await this.debugLog(`version: ${deviceVersion}`) } } @@ -551,22 +372,22 @@ export class Bot extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as botServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as botServiceData // Update HomeKit if (serviceData.model === SwitchBotBLEModel.Bot && serviceData.modelName === SwitchBotBLEModelName.Bot) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -587,7 +408,7 @@ export class Bot extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -601,17 +422,17 @@ export class Bot extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -629,7 +450,7 @@ export class Bot extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -666,21 +487,22 @@ export class Bot extends deviceBase { async BLEpushChanges(): Promise { await this.debugLog('BLEpushChanges') - if (this.On !== this.accessory.context.On || this.allowPush) { + if ((this.On !== this.accessory.context.On) || this.allowPush) { await this.debugLog(`BLEpushChanges On: ${this.On} OnCached: ${this.accessory.context.On}`) - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = this.platform.switchBotBLE try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - // if (switchbot !== false) { + // if (switchBotBLE !== false) { await this.debugLog(`Bot Mode: ${this.botMode}`) if (this.botMode === 'press') { - switchbot + switchBotBLE .discover({ model: 'H', quick: true, id: this.device.bleMac }) - .then(async (device_list: { press: (arg0: { id: string | undefined }) => any }[]) => { + .then(async (device_list: SwitchbotDevice[]) => { + const deviceList = device_list as unknown as WoHand[] await this.infoLog(`On: ${this.On}`) - return await device_list[0].press({ id: this.device.bleMac }) + return await deviceList[0].press() }) .then(async () => { await this.successLog(`On: ${this.On} sent over SwitchBot BLE, sent successfully`) @@ -697,17 +519,23 @@ export class Bot extends deviceBase { await this.BLEPushConnection() }) } else if (this.botMode === 'switch') { - switchbot + switchBotBLE .discover({ model: this.device.bleModel, quick: true, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: SwitchbotDevice[]) => { + const deviceList = device_list as unknown as WoHand[] this.infoLog(`On: ${this.On}`) + this.warnLog(`device_list: ${JSON.stringify(device_list)}`) return await this.retryBLE({ max: await this.maxRetryBLE(), fn: async () => { - if (this.On) { - return await device_list[0].turnOn({ id: this.device.bleMac }) + if (deviceList.length > 0) { + if (this.On) { + return await deviceList[0].turnOn() + } else { + return await deviceList[0].turnOff() + } } else { - return await device_list[0].turnOff({ id: this.device.bleMac }) + throw new Error('No device found') } }, }) @@ -737,7 +565,7 @@ export class Bot extends deviceBase { if (this.multiPressCount > 0) { await this.debugLog(`${this.multiPressCount} request(s) queued.`) } - if (this.On !== this.accessory.context.On || this.allowPush || this.multiPressCount > 0) { + if ((this.On !== this.accessory.context.On) || this.allowPush || this.multiPressCount > 0) { let command = '' if (this.botMode === 'switch') { command = this.On ? 'turnOn' : 'turnOff' @@ -749,24 +577,23 @@ export class Bot extends deviceBase { } else { throw new Error('Device Parameters not set for this Bot.') } - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: `${command}`, parameter: 'default', commandType: 'command', - }) - this.debugLog(`Sending request to SwitchBot API, body: ${bodyChange},`) + } + this.debugLog(`Sending request to SwitchBot API, body: ${JSON.stringify(bodyChange)},`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } - if (this.device.bot?.mode === 'multipress') { + if ((this.device as botConfig).mode === 'multipress') { this.multiPressCount-- if (this.multiPressCount > 0) { await this.debugLog(`multiPressCount: ${this.multiPressCount}`) @@ -787,71 +614,27 @@ export class Bot extends deviceBase { * Handle requests to set the "On" characteristic */ async OnSet(value: CharacteristicValue): Promise { - this.On = this.accessory.context.On ?? false - this.accessory.context.On = this.On - const deviceTypeActions: { [key: string]: () => Promise } = { - switch: async () => { - if (this.Switch) { - await this.debugLog(`Set On: ${value}`) - this.On = value !== false - } - }, - garagedoor: async () => { - if (this.GarageDoor) { - await this.debugLog(`Set TargetDoorState: ${value}`) - this.On = value !== this.hap.Characteristic.TargetDoorState.CLOSED - } - }, - door: async () => { - if (this.Door) { - await this.debugLog(`Set TargetPosition: ${value}`) - this.On = value !== 0 - } - }, - window: async () => { - if (this.Window) { - await this.debugLog(`Set TargetPosition: ${value}`) - this.On = value !== 0 - } - }, - windowcovering: async () => { - if (this.WindowCovering) { - await this.debugLog(`Set TargetPosition: ${value}`) - this.On = value !== 0 - } - }, - lock: async () => { - if (this.LockMechanism) { - await this.debugLog(`Set LockTargetState: ${value}`) - this.On = value !== this.hap.Characteristic.LockTargetState.SECURED - } - }, - faucet: async () => { - if (this.Faucet) { - await this.debugLog(`Set Active: ${value}`) - this.On = value !== this.hap.Characteristic.Active.INACTIVE - } - }, - stateful: async () => { - if (this.StatefulProgrammableSwitch) { - await this.debugLog(`Set ProgrammableSwitchOutputState: ${value}`) - this.On = value !== 0 - } - }, - default: async () => { - if (this.Outlet) { - await this.debugLog(`Set On: ${value}`) - this.On = value !== false - } - if (this.device.bot?.mode === 'multipress' && this.On) { + this.debugLog(`value: ${value}`) + const deviceTypeActions: { [key: string]: () => void } = { + switch: () => this.On = value !== false, + garagedoor: () => this.On = value !== this.hap.Characteristic.TargetDoorState.CLOSED, + door: () => this.On = value !== 0, + window: () => this.On = value !== 0, + windowcovering: () => this.On = value !== 0, + lock: () => this.On = value !== this.hap.Characteristic.LockTargetState.SECURED, + faucet: () => this.On = value !== this.hap.Characteristic.Active.INACTIVE, + fan: () => this.On = value !== 0, + stateful: () => this.On = value !== 0, + default: () => { + this.On = value !== false + if ((this.device as botConfig).mode === 'multipress' && this.On) { this.multiPressCount++ - await this.debugLog(`multiPressCount: ${this.multiPressCount}`) + this.debugLog(`multiPressCount: ${this.multiPressCount}`) } }, } const action = deviceTypeActions[this.botDeviceType] || deviceTypeActions.default - await action() - this.accessory.context.On = this.On + action() this.doBotUpdate.next() } @@ -877,312 +660,160 @@ export class Bot extends deviceBase { await this.debugLog(`updateCharacteristic StatusLowBattery: ${this.Battery.StatusLowBattery}`) } // State - if (this.botDeviceType === 'switch' && this.Switch) { - if (this.On === undefined) { - await this.debugLog(`On: ${this.On}`) - } else { - this.Switch.Service.updateCharacteristic(this.hap.Characteristic.On, this.On) - await this.debugLog(`updateCharacteristic On: ${this.On}`) - } - } else if (this.botDeviceType === 'garagedoor' && this.GarageDoor) { - if (this.On === undefined) { - await this.debugLog(`On: ${this.On}`) - } else { - if (this.On) { - this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.OPEN) - this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.OPEN) - await this.debugLog(`updateCharacteristic TargetDoorState: Open, CurrentDoorState: Open (${this.On})`) - } else { - this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.CLOSED) - this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.CLOSED) - await this.debugLog(`updateCharacteristicc TargetDoorState: Closed, CurrentDoorState: Closed (${this.On})`) - } - } - await this.debugLog(`Garage Door On: ${this.On}`) - } else if (this.botDeviceType === 'door' && this.Door) { - if (this.On === undefined) { - await this.debugLog(`On: ${this.On}`) - } else { - if (this.On) { - this.Door.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100) - this.Door.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100) - this.Door.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) - await this.debugLog(`updateCharacteristicc TargetPosition: 100, CurrentPosition: 100 (${this.On})`) - } else { - this.Door.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0) - this.Door.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0) - this.Door.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) - await this.debugLog(`updateCharacteristicc TargetPosition: 0, CurrentPosition: 0 (${this.On})`) - } - } - await this.debugLog(`Door On: ${this.On}`) - } else if (this.botDeviceType === 'window' && this.Window) { - if (this.On === undefined) { - await this.debugLog(`On: ${this.On}`) - } else { - if (this.On) { - this.Window.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100) - this.Window.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100) - this.Window.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) - await this.debugLog(`updateCharacteristicc TargetPosition: 100, CurrentPosition: 100 (${this.On})`) - } else { - this.Window.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0) - this.Window.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0) - this.Window.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) - await this.debugLog(`updateCharacteristicc TargetPosition: 0, CurrentPosition: 0 (${this.On})`) + const updateCharacteristic = async (service: Service, characteristic: any, value: any, logMessage: string) => { + service.updateCharacteristic(characteristic, value) + await this.debugLog(logMessage) + } + + const stateActions: { [key: string]: () => Promise } = { + switch: async () => this.Switch && await updateCharacteristic(this.Switch.Service, this.hap.Characteristic.On, this.On, `updateCharacteristic On: ${this.On}`), + garagedoor: async () => { + if (this.GarageDoor) { + const targetState = this.On ? this.hap.Characteristic.TargetDoorState.OPEN : this.hap.Characteristic.TargetDoorState.CLOSED + const currentState = this.On ? this.hap.Characteristic.CurrentDoorState.OPEN : this.hap.Characteristic.CurrentDoorState.CLOSED + await updateCharacteristic(this.GarageDoor.Service, this.hap.Characteristic.TargetDoorState, targetState, `updateCharacteristic TargetDoorState: ${targetState}, CurrentDoorState: ${currentState} (${this.On})`) + await updateCharacteristic(this.GarageDoor.Service, this.hap.Characteristic.CurrentDoorState, currentState, '') } - } - await this.debugLog(`Window On: ${this.On}`) - } else if (this.botDeviceType === 'windowcovering' && this.WindowCovering) { - if (this.On === undefined) { - await this.debugLog(`On: ${this.On}`) - } else { - if (this.On) { - this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100) - this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100) - this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) - await this.debugLog(`updateCharacteristicc TargetPosition: 100, CurrentPosition: 100 (${this.On})`) - } else { - this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0) - this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0) - this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) - await this.debugLog(`updateCharacteristicc TargetPosition: 0, CurrentPosition: 0 (${this.On})`) + }, + door: async () => { + if (this.Door) { + const position = this.On ? 100 : 0 + await updateCharacteristic(this.Door.Service, this.hap.Characteristic.TargetPosition, position, `updateCharacteristic TargetPosition: ${position}, CurrentPosition: ${position} (${this.On})`) + await updateCharacteristic(this.Door.Service, this.hap.Characteristic.CurrentPosition, position, '') + await updateCharacteristic(this.Door.Service, this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED, '') } - } - await this.debugLog(`Window Covering On: ${this.On}`) - } else if (this.botDeviceType === 'lock' && this.LockMechanism) { - if (this.On === undefined) { - await this.debugLog(`On: ${this.On}`) - } else { - if (this.On) { - this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.UNSECURED) - this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.UNSECURED) - await this.debugLog(`updateCharacteristicc LockTargetState: UNSECURED, LockCurrentState: UNSECURED (${this.On})`) - } else { - this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED) - this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.SECURED) - await this.debugLog(`updateCharacteristic LockTargetState: SECURED, LockCurrentState: SECURED (${this.On})`) + }, + window: async () => { + if (this.Window) { + const position = this.On ? 100 : 0 + await updateCharacteristic(this.Window.Service, this.hap.Characteristic.TargetPosition, position, `updateCharacteristic TargetPosition: ${position}, CurrentPosition: ${position} (${this.On})`) + await updateCharacteristic(this.Window.Service, this.hap.Characteristic.CurrentPosition, position, '') + await updateCharacteristic(this.Window.Service, this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED, '') } - } - await this.debugLog(`Lock On: ${this.On}`) - } else if (this.botDeviceType === 'faucet' && this.Faucet) { - if (this.On === undefined) { - await this.debugLog(`On: ${this.On}`) - } else { - if (this.On) { - this.Faucet.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.ACTIVE) - await this.debugLog(`updateCharacteristic Active: ${this.On}`) - } else { - this.Faucet.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE) - await this.debugLog(`updateCharacteristic Active: ${this.On}`) + }, + windowcovering: async () => { + if (this.WindowCovering) { + const position = this.On ? 100 : 0 + await updateCharacteristic(this.WindowCovering.Service, this.hap.Characteristic.TargetPosition, position, `updateCharacteristic TargetPosition: ${position}, CurrentPosition: ${position} (${this.On})`) + await updateCharacteristic(this.WindowCovering.Service, this.hap.Characteristic.CurrentPosition, position, '') + await updateCharacteristic(this.WindowCovering.Service, this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED, '') } - } - await this.debugLog(`Faucet On: ${this.On}`) - } else if (this.botDeviceType === 'fan' && this.Fan) { - if (this.On === undefined) { - await this.debugLog(`On: ${this.On}`) - } else { - if (this.On) { - this.Fan.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.ACTIVE) - await this.debugLog(`updateCharacteristic Active: ${this.On}`) - } else { - this.Fan.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE) - await this.debugLog(`updateCharacteristic Active: ${this.On}`) + }, + lock: async () => { + if (this.LockMechanism) { + const targetState = this.On ? this.hap.Characteristic.LockTargetState.UNSECURED : this.hap.Characteristic.LockTargetState.SECURED + const currentState = this.On ? this.hap.Characteristic.LockCurrentState.UNSECURED : this.hap.Characteristic.LockCurrentState.SECURED + await updateCharacteristic(this.LockMechanism.Service, this.hap.Characteristic.LockTargetState, targetState, `updateCharacteristic LockTargetState: ${targetState}, LockCurrentState: ${currentState} (${this.On})`) + await updateCharacteristic(this.LockMechanism.Service, this.hap.Characteristic.LockCurrentState, currentState, '') } - } - await this.debugLog(`Fan On: ${this.On}`) - } else if (this.botDeviceType === 'stateful' && this.StatefulProgrammableSwitch) { - if (this.On === undefined) { - await this.debugLog(`On: ${this.On}`) - } else { - if (this.On) { - this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS) - this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 1) - await this.debugLog(`updateCharacteristic ProgrammableSwitchEvent: ProgrammableSwitchOutputState: (${this.On})`) - } else { - this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS) - this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 0) - await this.debugLog(`updateCharacteristic ProgrammableSwitchEvent: ProgrammableSwitchOutputState: (${this.On})`) + }, + faucet: async () => this.Faucet && await updateCharacteristic(this.Faucet.Service, this.hap.Characteristic.Active, this.On ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE, `updateCharacteristic Active: ${this.On}`), + fan: async () => this.Fan && await updateCharacteristic(this.Fan.Service, this.hap.Characteristic.Active, this.On ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE, `updateCharacteristic Active: ${this.On}`), + stateful: async () => { + if (this.StatefulProgrammableSwitch) { + await updateCharacteristic(this.StatefulProgrammableSwitch.Service, this.hap.Characteristic.ProgrammableSwitchEvent, this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS, `updateCharacteristic ProgrammableSwitchEvent: SINGLE_PRESS (${this.On})`) + await updateCharacteristic(this.StatefulProgrammableSwitch.Service, this.hap.Characteristic.ProgrammableSwitchOutputState, this.On ? 1 : 0, `updateCharacteristic ProgrammableSwitchOutputState: ${this.On ? 1 : 0}`) } - } - await this.debugLog(`StatefulProgrammableSwitch On: ${this.On}`) - } else if (this.botDeviceType === 'outlet' && this.Outlet) { - if (this.On === undefined) { - await this.debugLog(`On: ${this.On}`) - } else { - this.Outlet.Service.updateCharacteristic(this.hap.Characteristic.On, this.On) - await this.debugLog(`updateCharacteristic On: ${this.On}`) - } - } else { - await this.errorLog(`botDeviceType: ${this.botDeviceType}, On: ${this.On}`) + }, + outlet: async () => this.Outlet && await updateCharacteristic(this.Outlet.Service, this.hap.Characteristic.On, this.On, `updateCharacteristic On: ${this.On}`), + default: async () => await this.errorLog(`botDeviceType: ${this.botDeviceType}, On: ${this.On}`), } + + const action = stateActions[this.botDeviceType] || stateActions.default + await action() } - async removeOutletService(accessory: PlatformAccessory): Promise { - // If outletService still present, then remove first - accessory.context.Outlet = accessory.context.Outlet ?? {} - this.Outlet = { - Name: accessory.context.Outlet.Name ?? accessory.displayName, - Service: accessory.getService(this.hap.Service.Outlet) as Service, + async removeService(accessory: PlatformAccessory, serviceType: string, serviceName: string): Promise { + const contextKey = serviceName.charAt(0).toUpperCase() + serviceName.slice(1) + accessory.context[contextKey] = accessory.context[contextKey] ?? {} + this[contextKey] = { + Name: accessory.context[contextKey].Name ?? accessory.displayName, + Service: accessory.getService(this.hap.Service[serviceType]) as Service, } - accessory.context.Outlet = this.Outlet as object - await this.debugWarnLog('Removing any leftover Outlet Service') - accessory.removeService(this.Outlet.Service) + accessory.context[contextKey] = this[contextKey] as object + await this.debugWarnLog(`Removing any leftover ${contextKey} Service`) + accessory.removeService(this[contextKey].Service) + } + + async removeOutletService(accessory: PlatformAccessory): Promise { + await this.removeService(accessory, 'Outlet', 'outlet') } async removeGarageDoorService(accessory: PlatformAccessory): Promise { - // If garageDoorService still present, then remove first - accessory.context.GarageDoor = accessory.context.GarageDoor ?? {} - this.GarageDoor = { - Name: accessory.context.GarageDoor.Name ?? accessory.displayName, - Service: accessory.getService(this.hap.Service.GarageDoorOpener) as Service, - } - accessory.context.GarageDoor = this.GarageDoor as object - await this.debugWarnLog('Removing any leftover Garage Door Service') - accessory.removeService(this.GarageDoor.Service) + await this.removeService(accessory, 'GarageDoorOpener', 'garageDoor') } async removeDoorService(accessory: PlatformAccessory): Promise { - // If doorService still present, then remove first - accessory.context.Door = accessory.context.Door ?? {} - this.Door = { - Name: accessory.context.Door.Name ?? accessory.displayName, - Service: accessory.getService(this.hap.Service.Door) as Service, - } - accessory.context.Door = this.Door as object - await this.debugWarnLog('Removing any leftover Door Service') - accessory.removeService(this.Door.Service) + await this.removeService(accessory, 'Door', 'door') } async removeLockService(accessory: PlatformAccessory): Promise { - // If lockService still present, then remove first - accessory.context.LockMechanism = accessory.context.LockMechanism ?? {} - this.LockMechanism = { - Name: accessory.context.LockMechanism.Name ?? accessory.displayName, - Service: accessory.getService(this.hap.Service.LockMechanism) as Service, - } - accessory.context.LockMechanism = this.LockMechanism as object - await this.debugWarnLog('Removing any leftover Lock Service') - accessory.removeService(this.LockMechanism.Service) + await this.removeService(accessory, 'LockMechanism', 'lockMechanism') } async removeFaucetService(accessory: PlatformAccessory): Promise { - // If faucetService still present, then remove first - accessory.context.Faucet = accessory.context.Faucet ?? {} - this.Faucet = { - Name: accessory.context.Faucet.Name ?? accessory.displayName, - Service: accessory.getService(this.hap.Service.Valve) as Service, - } - accessory.context.Faucet = this.Faucet as object - await this.debugWarnLog('Removing any leftover Faucet Service') - accessory.removeService(this.Faucet.Service) + await this.removeService(accessory, 'Valve', 'faucet') } async removeFanService(accessory: PlatformAccessory): Promise { - // If fanService still present, then remove first - accessory.context.Fan = accessory.context.Fan ?? {} - this.Fan = { - Name: accessory.context.Fan.Name ?? accessory.displayName, - Service: accessory.getService(this.hap.Service.Fan) as Service, - } - accessory.context.Fan = this.Fan as object - await this.debugWarnLog('Removing any leftover Fan Service') - accessory.removeService(this.Fan.Service) + await this.removeService(accessory, 'Fan', 'fan') } async removeWindowService(accessory: PlatformAccessory): Promise { - // If windowService still present, then remove first - accessory.context.Window = accessory.context.Window ?? {} - this.Window = { - Name: accessory.context.Window.Name ?? accessory.displayName, - Service: accessory.getService(this.hap.Service.Window) as Service, - } - accessory.context.Window = this.Window as object - await this.debugWarnLog('Removing any leftover Window Service') - accessory.removeService(this.Window.Service) + await this.removeService(accessory, 'Window', 'window') } async removeWindowCoveringService(accessory: PlatformAccessory): Promise { - // If windowCoveringService still present, then remove first - accessory.context.WindowCovering = accessory.context.WindowCovering ?? {} - this.WindowCovering = { - Name: accessory.context.WindowCovering.Name ?? accessory.displayName, - Service: accessory.getService(this.hap.Service.WindowCovering) as Service, - } - accessory.context.WindowCovering = this.WindowCovering as object - await this.debugWarnLog('Removing any leftover Window Covering Service') - accessory.removeService(this.WindowCovering.Service) + await this.removeService(accessory, 'WindowCovering', 'windowCovering') } async removeStatefulProgrammableSwitchService(accessory: PlatformAccessory): Promise { - // If statefulProgrammableSwitchService still present, then remove first - accessory.context.StatefulProgrammableSwitch = accessory.context.StatefulProgrammableSwitch ?? {} - this.StatefulProgrammableSwitch = { - Name: accessory.context.StatefulProgrammableSwitch.Name ?? accessory.displayName, - Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service, - } - accessory.context.StatefulProgrammableSwitch = this.StatefulProgrammableSwitch as object - await this.debugWarnLog('Removing any leftover Stateful Programmable Switch Service') - accessory.removeService(this.StatefulProgrammableSwitch.Service) + await this.removeService(accessory, 'StatefulProgrammableSwitch', 'statefulProgrammableSwitch') } async removeSwitchService(accessory: PlatformAccessory): Promise { - // If switchService still present, then remove first - accessory.context.Switch = accessory.context.Switch ?? {} - this.Switch = { - Name: accessory.context.Switch.Name ?? accessory.displayName, - Service: accessory.getService(this.hap.Service.Switch) as Service, - } - accessory.context.Switch = this.Switch as object - await this.debugWarnLog('Removing any leftover Switch Service') - accessory.removeService(this.Switch.Service) + await this.removeService(accessory, 'Switch', 'switch') } async getBotConfigSettings(device: device & devicesConfig) { // Bot Device Type - this.botDeviceType = device.bot?.deviceType ?? 'outlet' - const botDeviceType = device.bot?.deviceType - ? `Using Device Type: ${this.botDeviceType}` - : `No Device Type Set, deviceType: ${this.device.bot?.deviceType}, Using default deviceType: ${this.botDeviceType}` - await this.debugWarnLog(botDeviceType) - this.accessory.context.botDeviceType = this.botDeviceType + this.botDeviceType = (device as botConfig).type ?? 'outlet' + const botDeviceType = (device as botConfig).type ? 'Device Config' : 'Default' + await this.debugWarnLog(`Use ${botDeviceType} Device Type: ${this.botDeviceType}`) // Bot Mode - this.botMode = device.bot?.mode ?? 'switch' - if (!device.bot?.mode) { + this.botMode = (device as botConfig).mode ?? 'switch' + if (!(device as botConfig).mode) { this.botMode = 'switch' this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} does not have bot mode set in the Plugin's SwitchBot Device Settings, defaulting to "${this.botMode}" mode. You may experience issues.`) - } else if (['switch', 'press', 'multipress'].includes(device.bot.mode)) { - this.botMode = device.bot.mode + } else if (['switch', 'press', 'multipress'].includes((device as botConfig).mode!)) { + this.botMode = (device as botConfig).mode! this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Bot Mode: ${this.botMode}`) } else { - throw new Error(`${this.device.deviceType}: ${this.accessory.displayName} Invalid Bot Mode: ${device.bot.mode}`) + throw new Error(`${this.device.deviceType}: ${this.accessory.displayName} Invalid Bot Mode: ${(device as botConfig).mode}`) } - const botModeLog = device.bot?.mode + const botModeLog = (device as botConfig).mode ? `Using Bot Mode: ${this.botMode}` : `No Bot Mode Set, Using default Bot Mode: ${this.botMode}` await this.debugWarnLog(botModeLog) this.accessory.context.botMode = this.botMode // Bot Double Press - this.doublePress = device.bot?.doublePress ?? 1 - const doublePress = device.bot?.doublePress + this.doublePress = (device as botConfig).doublePress ?? 1 + const doublePress = (device as botConfig).doublePress ? `Using Double Press: ${this.doublePress}` : `No Double Press Set, Using default Double Press: ${this.doublePress}` await this.debugWarnLog(doublePress) - this.accessory.context.doublePress = this.doublePress // Bot Press PushRate - this.pushRatePress = device.bot?.pushRatePress ?? 15 - const pushRatePress = device.bot?.pushRatePress + this.pushRatePress = (device as botConfig).pushRatePress ?? 15 + const pushRatePress = (device as botConfig).pushRatePress ? `Using Bot Push Rate Press: ${this.pushRatePress}` : `No Push Rate Press Set, Using default Push Rate Press: ${this.pushRatePress}` await this.debugWarnLog(pushRatePress) - this.accessory.context.pushRatePress = this.pushRatePress // Bot Allow Push - this.allowPush = device.bot?.allowPush ?? false - const allowPush = device.bot?.allowPush + this.allowPush = (device as botConfig).allowPush ?? false + const allowPush = (device as botConfig).allowPush ? `Using Allow Push: ${this.allowPush}` : `No Allow Push Set, Using default Allow Push: ${this.allowPush}` await this.debugWarnLog(allowPush) - this.accessory.context.allowPush = this.allowPush // Bot Multi Press Count this.multiPressCount = 0 await this.debugWarnLog(`Multi Press Count: ${this.multiPressCount}`) @@ -1205,113 +836,58 @@ export class Bot extends deviceBase { async offlineOff(): Promise { if (this.device.offline) { - if (this.botDeviceType === 'garagedoor') { - if (this.GarageDoor) { - this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, this.hap.Characteristic.TargetDoorState.CLOSED) - this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, this.hap.Characteristic.CurrentDoorState.CLOSED) - this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, false) - } - } else if (this.botDeviceType === 'door') { - if (this.Door) { - this.Door.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0) - this.Door.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0) - this.Door.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) - } - } else if (this.botDeviceType === 'window') { - if (this.Window) { - this.Window.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0) - this.Window.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0) - this.Window.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) - } - } else if (this.botDeviceType === 'windowcovering') { - if (this.WindowCovering) { - this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0) - this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0) - this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED) - } - } else if (this.botDeviceType === 'lock') { - if (this.LockMechanism) { - this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED) - this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.SECURED) - } - } else if (this.botDeviceType === 'faucet') { - if (this.Faucet) { - this.Faucet.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE) - } - } else if (this.botDeviceType === 'fan') { - if (this.Fan) { - this.Fan.Service.updateCharacteristic(this.hap.Characteristic.On, false) - } - } else if (this.botDeviceType === 'stateful') { - if (this.StatefulProgrammableSwitch) { - this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS) - this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 0) - } - } else if (this.botDeviceType === 'switch') { - if (this.Switch) { - this.Switch.Service.updateCharacteristic(this.hap.Characteristic.On, false) - } - } else { - if (this.Outlet) { - this.Outlet.Service.updateCharacteristic(this.hap.Characteristic.On, false) + const updateCharacteristics = (service: Service, characteristics: { [key: string]: any }) => { + for (const [characteristic, value] of Object.entries(characteristics)) { + service.updateCharacteristic(this.hap.Characteristic[characteristic], value) } } + + const characteristicsMap: { [key: string]: { [key: string]: any } } = { + garagedoor: { TargetDoorState: 'CLOSED', CurrentDoorState: 'CLOSED', ObstructionDetected: false }, + door: { TargetPosition: 0, CurrentPosition: 0, PositionState: 'STOPPED' }, + window: { TargetPosition: 0, CurrentPosition: 0, PositionState: 'STOPPED' }, + windowcovering: { TargetPosition: 0, CurrentPosition: 0, PositionState: 'STOPPED' }, + lock: { LockTargetState: 'SECURED', LockCurrentState: 'SECURED' }, + faucet: { Active: 'INACTIVE' }, + fan: { On: false }, + stateful: { ProgrammableSwitchEvent: 'SINGLE_PRESS', ProgrammableSwitchOutputState: 0 }, + switch: { On: false }, + outlet: { On: false }, + } + + const service = this[this.botDeviceType.charAt(0).toUpperCase() + this.botDeviceType.slice(1)]?.Service + if (service) { + updateCharacteristics(service, characteristicsMap[this.botDeviceType]) + } } } async apiError(e: any): Promise { + const updateCharacteristics = (service: Service, characteristics: { [key: string]: any }) => { + for (const [characteristic] of Object.entries(characteristics)) { + service.updateCharacteristic(this.hap.Characteristic[characteristic], e) + } + } + + const characteristicsMap: { [key: string]: { [key: string]: any } } = { + garagedoor: { TargetDoorState: e, CurrentDoorState: e, ObstructionDetected: e }, + door: { TargetPosition: e, CurrentPosition: e, PositionState: e }, + window: { TargetPosition: e, CurrentPosition: e, PositionState: e }, + windowcovering: { TargetPosition: e, CurrentPosition: e, PositionState: e }, + lock: { LockTargetState: e, LockCurrentState: e }, + faucet: { Active: e }, + fan: { On: e }, + stateful: { ProgrammableSwitchEvent: e, ProgrammableSwitchOutputState: e }, + switch: { On: e }, + outlet: { On: e }, + } + this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e) this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e) - if (this.botDeviceType === 'garagedoor') { - if (this.GarageDoor) { - this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.TargetDoorState, e) - this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, e) - this.GarageDoor.Service.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, e) - } - } else if (this.botDeviceType === 'door') { - if (this.Door) { - this.Door.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e) - this.Door.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e) - this.Door.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e) - } - } else if (this.botDeviceType === 'window') { - if (this.Window) { - this.Window.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e) - this.Window.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e) - this.Window.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e) - } - } else if (this.botDeviceType === 'windowcovering') { - if (this.WindowCovering) { - this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e) - this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e) - this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e) - } - } else if (this.botDeviceType === 'lock') { - if (this.LockMechanism) { - this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, e) - this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, e) - } - } else if (this.botDeviceType === 'faucet') { - if (this.Faucet) { - this.Faucet.Service.updateCharacteristic(this.hap.Characteristic.Active, e) - } - } else if (this.botDeviceType === 'fan') { - if (this.Fan) { - this.Fan.Service.updateCharacteristic(this.hap.Characteristic.On, e) - } - } else if (this.botDeviceType === 'stateful') { - if (this.StatefulProgrammableSwitch) { - this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e) - this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e) - } - } else if (this.botDeviceType === 'switch') { - if (this.Switch) { - this.Switch.Service.updateCharacteristic(this.hap.Characteristic.On, e) - } - } else { - if (this.Outlet) { - this.Outlet.Service.updateCharacteristic(this.hap.Characteristic.On, e) - } + + const service = this[this.botDeviceType.charAt(0).toUpperCase() + this.botDeviceType.slice(1)]?.Service + if (service) { + updateCharacteristics(service, characteristicsMap[this.botDeviceType]) } } } diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts index 8b94dfa0..b1215a5d 100644 --- a/src/device/ceilinglight.ts +++ b/src/device/ceilinglight.ts @@ -3,13 +3,10 @@ * ceilinglight.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, Controller, ControllerConstructor, ControllerServiceMap, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, ceilingLightProServiceData, ceilingLightProStatus, ceilingLightProWebhookContext, ceilingLightServiceData, ceilingLightStatus, ceilingLightWebhookContext, device, SwitchbotDevice, WoCeilingLight } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { ceilingLightProServiceData, ceilingLightServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { ceilingLightProStatus, ceilingLightStatus } from '../types/devicestatus.js' -import type { ceilingLightProWebhookContext, ceilingLightWebhookContext } from '../types/devicewebhookstatus.js' +import type { ceilingLightConfig, devicesConfig } from '../settings.js' /* * For Testing Locally: @@ -111,7 +108,7 @@ export class CeilingLight extends deviceBase { // Initialize LightBulb Brightness this.LightBulb.Service.getCharacteristic(this.hap.Characteristic.Brightness).setProps({ - minStep: device.ceilinglight?.set_minStep ?? 1, + minStep: (device as ceilingLightConfig).set_minStep ?? 1, minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -151,7 +148,7 @@ export class CeilingLight extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -159,7 +156,7 @@ export class CeilingLight extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -167,7 +164,7 @@ export class CeilingLight extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -286,21 +283,21 @@ export class CeilingLight extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as unknown as ceilingLightServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as unknown as ceilingLightServiceData // Update HomeKit if ((serviceData.model === SwitchBotBLEModel.CeilingLight || SwitchBotBLEModel.CeilingLightPro) && (serviceData.modelName === SwitchBotBLEModelName.CeilingLight || SwitchBotBLEModelName.CeilingLightPro)) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -321,7 +318,7 @@ export class CeilingLight extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -335,17 +332,17 @@ export class CeilingLight extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -363,7 +360,7 @@ export class CeilingLight extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -420,23 +417,24 @@ export class CeilingLight extends deviceBase { await this.debugLog('BLEpushChanges') if (this.LightBulb.On !== this.accessory.context.On) { await this.debugLog(`BLEpushChanges On: ${this.LightBulb.On} OnCached: ${this.accessory.context.On}`) - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: SwitchbotDevice[]) => { + const deviceList = device_list[0] as unknown as WoCeilingLight this.infoLog(`On: ${this.LightBulb.On}`) return await this.retryBLE({ max: await this.maxRetryBLE(), fn: async () => { if (this.LightBulb.On) { - return await device_list[0].turnOn({ id: this.device.bleMac }) + return await deviceList[0].turnOn() } else { - return await device_list[0].turnOff({ id: this.device.bleMac }) + return await deviceList[0].turnOff() } }, }) @@ -451,7 +449,7 @@ export class CeilingLight extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -466,21 +464,20 @@ export class CeilingLight extends deviceBase { await this.debugLog('openAPIpushChanges') if (this.LightBulb.On !== this.accessory.context.On) { const command = this.LightBulb.On ? 'turnOn' : 'turnOff' - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: `${command}`, parameter: 'default', commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -499,21 +496,20 @@ export class CeilingLight extends deviceBase { await this.debugLog(`Saturation: ${JSON.stringify(this.LightBulb.Saturation)}`) const [red, green, blue] = hs2rgb(Number(this.LightBulb.Hue), Number(this.LightBulb.Saturation)) await this.debugLog(`rgb: ${JSON.stringify([red, green, blue])}`) - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'setColor', parameter: `${red}:${green}:${blue}`, commandType: 'command', - }) + } await this.debugLog(`(pushHueSaturationChanges) SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`(pushHueSaturationChanges) statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`(pushHueSaturationChanges) statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`(pushHueSaturationChanges) statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`(pushHueSaturationChanges) statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -530,21 +526,20 @@ export class CeilingLight extends deviceBase { if (this.LightBulb.ColorTemperature !== this.accessory.context.ColorTemperature) { const kelvin = Math.round(1000000 * Number(this.LightBulb.ColorTemperature)) this.accessory.context.kelvin = kelvin - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'setColorTemperature', parameter: `${kelvin}`, commandType: 'command', - }) + } await this.debugLog(`(pushColorTemperatureChanges) SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`(pushColorTemperatureChanges) statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`(pushColorTemperatureChanges) statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`(pushColorTemperatureChanges) statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`(pushColorTemperatureChanges) statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -559,21 +554,20 @@ export class CeilingLight extends deviceBase { async pushBrightnessChanges(): Promise { await this.debugLog('pushBrightnessChanges') if (this.LightBulb.Brightness !== this.accessory.context.Brightness) { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'setBrightness', parameter: `${this.LightBulb.Brightness}`, commandType: 'command', - }) + } await this.debugLog(`(pushBrightnessChanges) SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`(pushBrightnessChanges) statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`(pushBrightnessChanges) statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`(pushBrightnessChanges) statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`(pushBrightnessChanges) statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -709,13 +703,8 @@ export class CeilingLight extends deviceBase { this.adaptiveLighting = accessory.context.adaptiveLighting ?? true await this.debugLog(`adaptiveLighting: ${this.adaptiveLighting}`) // Adaptive Lighting Shift - if (device.ceilinglight?.adaptiveLightingShift) { - this.adaptiveLightingShift = device.ceilinglight.adaptiveLightingShift - this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`) - } else { - this.adaptiveLightingShift = 0 - this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`) - } + this.adaptiveLightingShift = (device as ceilingLightConfig).adaptiveLightingShift ?? 0 + this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`) } async BLEPushConnection() { diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts index 77a0cc5a..7081c895 100644 --- a/src/device/colorbulb.ts +++ b/src/device/colorbulb.ts @@ -3,13 +3,10 @@ * blindtilt.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, Controller, ControllerConstructor, ControllerServiceMap, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, colorBulbServiceData, colorBulbStatus, colorBulbWebhookContext, device, SwitchbotDevice, WoBulb } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { colorBulbServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { colorBulbStatus } from '../types/devicestatus.js' -import type { colorBulbWebhookContext } from '../types/devicewebhookstatus.js' +import type { colorBulbConfig, devicesConfig } from '../settings.js' /* * For Testing Locally: @@ -110,7 +107,7 @@ export class ColorBulb extends deviceBase { }).onSet(this.OnSet.bind(this)) this.LightBulb.Service.getCharacteristic(this.hap.Characteristic.Brightness).setProps({ - minStep: device.colorbulb?.set_minStep ?? 1, + minStep: (device as colorBulbConfig)?.set_minStep ?? 1, minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -147,7 +144,7 @@ export class ColorBulb extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -155,7 +152,7 @@ export class ColorBulb extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -163,7 +160,7 @@ export class ColorBulb extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -308,22 +305,22 @@ export class ColorBulb extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as colorBulbServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as colorBulbServiceData // Update HomeKit if (serviceData.model === SwitchBotBLEModel.ColorBulb && serviceData.modelName === SwitchBotBLEModelName.ColorBulb) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -344,7 +341,7 @@ export class ColorBulb extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -358,17 +355,17 @@ export class ColorBulb extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -386,7 +383,7 @@ export class ColorBulb extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -455,23 +452,23 @@ export class ColorBulb extends deviceBase { await this.debugLog('BLEpushChanges') if (this.LightBulb.On !== this.accessory.context.On) { await this.debugLog(`BLEpushChanges On: ${this.LightBulb.On}, OnCached: ${this.accessory.context.On}`) - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: WoBulb[]) => { await this.infoLog(`On: ${this.LightBulb.On}`) return await this.retryBLE({ max: await this.maxRetryBLE(), fn: async () => { if (this.LightBulb.On) { - return await device_list[0].turnOn({ id: this.device.bleMac }) + return await device_list[0].turnOn() } else { - return await device_list[0].turnOff({ id: this.device.bleMac }) + return await device_list[0].turnOff() } }, }) @@ -486,7 +483,7 @@ export class ColorBulb extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -500,17 +497,18 @@ export class ColorBulb extends deviceBase { async BLEpushBrightnessChanges(): Promise { await this.debugLog('BLEpushBrightnessChanges') if (this.LightBulb.Brightness !== this.accessory.context.Brightness) { - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: SwitchbotDevice[]) => { + const deviceList = device_list as unknown as WoBulb[] await this.infoLog(`Target Brightness: ${this.LightBulb.Brightness}`) - return await device_list[0].setBrightness(this.LightBulb.Brightness) + return await deviceList[0].setBrightness(Number(this.LightBulb.Brightness)) }) .then(async () => { await this.successLog(`Brightness: ${this.LightBulb.Brightness} sent over SwitchBot BLE, sent successfully`) @@ -522,7 +520,7 @@ export class ColorBulb extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -538,17 +536,18 @@ export class ColorBulb extends deviceBase { if (this.LightBulb.ColorTemperature !== this.accessory.context.ColorTemperature) { const kelvin = Math.round(1000000 / Number(this.LightBulb.ColorTemperature)) this.accessory.context.kelvin = kelvin - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: SwitchbotDevice[]) => { + const deviceList = device_list as unknown as WoBulb[] await this.infoLog(`ColorTemperature: ${this.LightBulb.ColorTemperature}`) - return await device_list[0].setColorTemperature(kelvin) + return await deviceList[0].setColorTemperature(kelvin) }) .then(async () => { await this.successLog(`ColorTemperature: ${this.LightBulb.ColorTemperature} sent over SwitchBot BLE, sent successfully`) @@ -560,7 +559,7 @@ export class ColorBulb extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -577,17 +576,18 @@ export class ColorBulb extends deviceBase { await this.debugLog(`Hue: ${JSON.stringify(this.LightBulb.Hue)}, Saturation: ${JSON.stringify(this.LightBulb.Saturation)}`) const [red, green, blue] = hs2rgb(this.LightBulb.Hue, this.LightBulb.Saturation) await this.debugLog(`rgb: ${JSON.stringify([red, green, blue])}`) - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: SwitchbotDevice[]) => { + const deviceList = device_list as unknown as WoBulb[] await this.infoLog(`RGB: ${(this.LightBulb.Brightness, red, green, blue)}`) - return await device_list[0].setRGB(this.LightBulb.Brightness, red, green, blue) + return await deviceList[0].setRGB(Number(this.LightBulb.Brightness), red, green, blue) }) .then(async () => { await this.successLog(`RGB: ${(this.LightBulb.Brightness, red, green, blue)} sent over SwitchBot BLE, sent successfully`) @@ -599,7 +599,7 @@ export class ColorBulb extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -614,21 +614,20 @@ export class ColorBulb extends deviceBase { await this.debugLog('openAPIpushChanges') if (this.LightBulb.On !== this.accessory.context.On) { const command = this.LightBulb.On ? 'turnOn' : 'turnOff' - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: `${command}`, parameter: 'default', commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -646,21 +645,20 @@ export class ColorBulb extends deviceBase { await this.debugLog(`Hue: ${JSON.stringify(this.LightBulb.Hue)}, Saturation: ${JSON.stringify(this.LightBulb.Saturation)}`) const [red, green, blue] = hs2rgb(this.LightBulb.Hue, this.LightBulb.Saturation) await this.debugLog(`rgb: ${JSON.stringify([red, green, blue])}`) - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'setColor', parameter: `${red}:${green}:${blue}`, commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -677,21 +675,20 @@ export class ColorBulb extends deviceBase { if (this.LightBulb.ColorTemperature !== this.accessory.context.ColorTemperature) { const kelvin = Math.round(1000000 / Number(this.LightBulb.ColorTemperature)) this.accessory.context.kelvin = kelvin - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'setColorTemperature', parameter: `${kelvin}`, commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -706,21 +703,20 @@ export class ColorBulb extends deviceBase { async pushBrightnessChanges(): Promise { await this.debugLog('pushBrightnessChanges') if (this.LightBulb.Brightness !== this.accessory.context.Brightness) { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'setBrightness', parameter: `${this.LightBulb.Brightness}`, commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -857,13 +853,8 @@ export class ColorBulb extends deviceBase { this.adaptiveLighting = accessory.context.adaptiveLighting ?? true await this.debugLog(`adaptiveLighting: ${this.adaptiveLighting}`) // Adaptive Lighting Shift - if (device.colorbulb?.adaptiveLightingShift) { - this.adaptiveLightingShift = device.colorbulb.adaptiveLightingShift - this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`) - } else { - this.adaptiveLightingShift = 0 - this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`) - } + this.adaptiveLightingShift = (device as colorBulbConfig).adaptiveLightingShift ?? 0 + this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`) } async BLEPushConnection() { diff --git a/src/device/contact.ts b/src/device/contact.ts index 4f453d2b..dcb071f9 100644 --- a/src/device/contact.ts +++ b/src/device/contact.ts @@ -3,13 +3,10 @@ * contact.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { contactSensorServiceData, contactSensorStatus, contactSensorWebhookContext, device } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { contactSensorServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { contactSensorStatus } from '../types/devicestatus.js' -import type { contactSensorWebhookContext } from '../types/devicewebhookstatus.js' +import type { contactConfig, devicesConfig } from '../settings.js' /* * For Testing Locally: @@ -113,7 +110,7 @@ export class Contact extends deviceBase { }) // Initialize Motion Sensor Service - if (this.device.contact?.hide_motionsensor) { + if ((this.device as contactConfig).hide_motionsensor) { if (this.MotionSensor) { this.debugLog('Removing Motion Sensor Service') this.MotionSensor.Service = accessory.getService(this.hap.Service.MotionSensor) as Service @@ -135,7 +132,7 @@ export class Contact extends deviceBase { } // Initialize Light Sensor Service - if (device.contact?.hide_lightsensor) { + if ((device as contactConfig).hide_lightsensor) { if (this.LightSensor) { this.debugLog('Removing Light Sensor Service') this.LightSensor.Service = accessory.getService(this.hap.Service.LightSensor) as Service @@ -161,7 +158,7 @@ export class Contact extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -169,7 +166,7 @@ export class Contact extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -177,7 +174,7 @@ export class Contact extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -195,14 +192,14 @@ export class Contact extends deviceBase { await this.debugLog(`ContactSensorState: ${this.ContactSensor.ContactSensorState}`) // MotionDetected - if (!this.device.contact?.hide_motionsensor && this.MotionSensor?.Service) { + if (!(this.device as contactConfig).hide_motionsensor && this.MotionSensor?.Service) { this.MotionSensor.MotionDetected = this.serviceData.movement await this.debugLog(`MotionDetected: ${this.MotionSensor.MotionDetected}`) } // CurrentAmbientLightLevel - if (!this.device.contact?.hide_lightsensor && this.LightSensor?.Service) { - const set_minLux = this.device.blindTilt?.set_minLux ?? 1 - const set_maxLux = this.device.blindTilt?.set_maxLux ?? 6001 + if (!(this.device as contactConfig).hide_lightsensor && this.LightSensor?.Service) { + const set_minLux = (this.device as contactConfig).set_minLux ?? 1 + const set_maxLux = (this.device as contactConfig).set_maxLux ?? 6001 const lightLevel = this.serviceData.lightLevel === 'bright' ? set_maxLux : set_minLux this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 2) await this.debugLog(`LightLevel: ${this.serviceData.lightLevel}, CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`) @@ -224,14 +221,14 @@ export class Contact extends deviceBase { await this.debugLog(`ContactSensorState: ${this.ContactSensor.ContactSensorState}`) // MotionDetected - if (!this.device.contact?.hide_motionsensor && this.MotionSensor?.Service) { + if (!(this.device as contactConfig).hide_motionsensor && this.MotionSensor?.Service) { this.MotionSensor.MotionDetected = this.deviceStatus.moveDetected await this.debugLog(`MotionDetected: ${this.MotionSensor.MotionDetected}`) } // Light Level - if (!this.device.contact?.hide_lightsensor && this.LightSensor?.Service) { - const set_minLux = this.device.blindTilt?.set_minLux ?? 1 - const set_maxLux = this.device.blindTilt?.set_maxLux ?? 6001 + if (!(this.device as contactConfig).hide_lightsensor && this.LightSensor?.Service) { + const set_minLux = (this.device as contactConfig).set_minLux ?? 1 + const set_maxLux = (this.device as contactConfig).set_maxLux ?? 6001 const lightLevel = this.deviceStatus.brightness === 'bright' ? set_maxLux : set_minLux this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 2) await this.debugLog(`LightLevel: ${this.deviceStatus.brightness}, CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`) @@ -266,14 +263,14 @@ export class Contact extends deviceBase { // ContactSensorState this.ContactSensor.ContactSensorState = this.getContactSensorState(this.webhookContext.openState) await this.debugLog(`ContactSensorState: ${this.ContactSensor.ContactSensorState}`) - if (!this.device.contact?.hide_motionsensor && this.MotionSensor?.Service) { + if (!(this.device as contactConfig).hide_motionsensor && this.MotionSensor?.Service) { // MotionDetected this.MotionSensor.MotionDetected = this.webhookContext.detectionState === 'DETECTED' await this.debugLog(`MotionDetected: ${this.MotionSensor.MotionDetected}`) } - if (!this.device.contact?.hide_lightsensor && this.LightSensor?.Service) { - const set_minLux = this.device.blindTilt?.set_minLux ?? 1 - const set_maxLux = this.device.blindTilt?.set_maxLux ?? 6001 + if (!(this.device as contactConfig).hide_lightsensor && this.LightSensor?.Service) { + const set_minLux = (this.device as contactConfig).set_minLux ?? 1 + const set_maxLux = (this.device as contactConfig).set_maxLux ?? 6001 const lightLevel = this.webhookContext.brightness === 'bright' ? set_maxLux : set_minLux this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 2) await this.debugLog(`LightLevel: ${this.webhookContext.brightness}, CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`) @@ -298,22 +295,22 @@ export class Contact extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as contactSensorServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as contactSensorServiceData // Update HomeKit if (serviceData.model === SwitchBotBLEModel.ContactSensor && serviceData.modelName === SwitchBotBLEModelName.ContactSensor) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -334,7 +331,7 @@ export class Contact extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -348,17 +345,17 @@ export class Contact extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -376,7 +373,7 @@ export class Contact extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -391,11 +388,11 @@ export class Contact extends deviceBase { // ContactSensorState await this.updateCharacteristic(this.ContactSensor.Service, this.hap.Characteristic.ContactSensorState, this.ContactSensor.ContactSensorState, 'ContactSensorState') // MotionDetected - if (!this.device.contact?.hide_motionsensor && this.MotionSensor?.Service) { + if (!(this.device as contactConfig).hide_motionsensor && this.MotionSensor?.Service) { await this.updateCharacteristic(this.MotionSensor.Service, this.hap.Characteristic.MotionDetected, this.MotionSensor.MotionDetected, 'MotionDetected') } // CurrentAmbientLightLevel - if (!this.device.contact?.hide_lightsensor && this.LightSensor?.Service) { + if (!(this.device as contactConfig).hide_lightsensor && this.LightSensor?.Service) { await this.updateCharacteristic(this.LightSensor.Service, this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor.CurrentAmbientLightLevel, 'CurrentAmbientLightLevel') } // BatteryLevel @@ -415,10 +412,10 @@ export class Contact extends deviceBase { async offlineOff(): Promise { if (this.device.offline) { this.ContactSensor.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED) - if (!this.device.contact?.hide_motionsensor && this.MotionSensor?.Service) { + if (!(this.device as contactConfig).hide_motionsensor && this.MotionSensor?.Service) { this.MotionSensor.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, false) } - if (!this.device.contact?.hide_lightsensor && this.LightSensor?.Service) { + if (!(this.device as contactConfig).hide_lightsensor && this.LightSensor?.Service) { this.LightSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, 100) } } @@ -427,11 +424,11 @@ export class Contact extends deviceBase { async apiError(e: any): Promise { this.ContactSensor.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, e) this.ContactSensor.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e) - if (!this.device.contact?.hide_motionsensor && this.MotionSensor?.Service) { + if (!(this.device as contactConfig).hide_motionsensor && this.MotionSensor?.Service) { this.MotionSensor.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, e) this.MotionSensor.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e) } - if (!this.device.contact?.hide_lightsensor && this.LightSensor?.Service) { + if (!(this.device as contactConfig).hide_lightsensor && this.LightSensor?.Service) { this.LightSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e) this.LightSensor.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e) } diff --git a/src/device/curtain.ts b/src/device/curtain.ts index 97557fd7..00b80f78 100644 --- a/src/device/curtain.ts +++ b/src/device/curtain.ts @@ -3,25 +3,23 @@ * curtain.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicChange, CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, curtain3ServiceData, curtain3WebhookContext, curtainServiceData, curtainStatus, curtainWebhookContext, device, SwitchbotDevice, WoCurtain } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { curtain3ServiceData, curtainServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { curtainStatus } from '../types/devicestatus.js' -import type { curtain3WebhookContext, curtainWebhookContext } from '../types/devicewebhookstatus.js' +import type { curtainConfig, devicesConfig } from '../settings.js' import { hostname } from 'node:os' +import { debounceTime, interval, skipWhile, Subject, take, tap } from 'rxjs' + +import { formatDeviceIdAsMac } from '../utils.js' +import { deviceBase } from './device.js' + /* * For Testing Locally: * import { SwitchBotBLEModel, SwitchBotBLEModelName } from '/Users/Shared/GitHub/OpenWonderLabs/node-switchbot/dist/index.js'; */ import { SwitchBotBLEModel, SwitchBotBLEModelName } from 'node-switchbot' -import { debounceTime, interval, skipWhile, Subject, take, tap } from 'rxjs' - -import { formatDeviceIdAsMac } from '../utils.js' -import { deviceBase } from './device.js' export class Curtain extends deviceBase { // Services @@ -113,7 +111,7 @@ export class Curtain extends deviceBase { // Initialize WindowCovering CurrentPosition this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.CurrentPosition).setProps({ - minStep: device.curtain?.set_minStep ?? 1, + minStep: (device as curtainConfig).set_minStep ?? 1, minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -123,7 +121,7 @@ export class Curtain extends deviceBase { // Initialize WindowCovering TargetPosition this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.TargetPosition).setProps({ - minStep: device.curtain?.set_minStep ?? 1, + minStep: (device as curtainConfig).set_minStep ?? 1, minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -161,7 +159,7 @@ export class Curtain extends deviceBase { }) // Initialize LightSensor Service - if (device.curtain?.hide_lightsensor || (device.deviceType !== 'curtain' && device.deviceType !== 'curtain3')) { + if ((device as curtainConfig).hide_lightsensor || (device.deviceType !== 'curtain' && device.deviceType !== 'curtain3')) { if (this.LightSensor?.Service) { this.debugLog('Removing Light Sensor Service') this.LightSensor.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service @@ -184,7 +182,7 @@ export class Curtain extends deviceBase { } // Initialize Open Mode Switch Service - if (!device.curtain?.silentModeSwitch) { + if (!(device as curtainConfig).silentModeSwitch) { if (this.OpenModeSwitch?.Service) { this.debugLog('Removing Open Mode Switch Service') this.OpenModeSwitch.Service = this.accessory.getService(this.hap.Service.Switch) as Service @@ -212,7 +210,7 @@ export class Curtain extends deviceBase { } // Initialize Close Mode Switch Service - if (!device.curtain?.silentModeSwitch) { + if (!(device as curtainConfig).silentModeSwitch) { if (this.CloseModeSwitch?.Service) { this.debugLog('Removing Close Mode Switch Service') this.CloseModeSwitch.Service = this.accessory.getService(this.hap.Service.Switch) as Service @@ -244,7 +242,7 @@ export class Curtain extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -252,7 +250,7 @@ export class Curtain extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -260,7 +258,7 @@ export class Curtain extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // History @@ -384,9 +382,9 @@ export class Curtain extends deviceBase { this.WindowCovering.CurrentPosition = 100 - this.serviceData.position await this.getCurrentPostion() // CurrentAmbientLightLevel - if (!this.device.curtain?.hide_lightsensor && this.LightSensor?.Service) { - const set_minLux = this.device.curtain?.set_minLux ?? 1 - const set_maxLux = this.device.curtain?.set_maxLux ?? 6001 + if (!(this.device as curtainConfig).hide_lightsensor && this.LightSensor?.Service) { + const set_minLux = (this.device as curtainConfig).set_minLux ?? 1 + const set_maxLux = (this.device as curtainConfig).set_maxLux ?? 6001 const lightLevel = this.serviceData.lightLevel this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 19) this.debugLog(`LightLevel: ${this.serviceData.lightLevel}, CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`) @@ -409,9 +407,9 @@ export class Curtain extends deviceBase { await this.getCurrentPostion() // Brightness - if (!this.device.curtain?.hide_lightsensor && this.LightSensor?.Service) { - const set_minLux = this.device.curtain?.set_minLux ?? 1 - const set_maxLux = this.device.curtain?.set_maxLux ?? 6001 + if (!(this.device as curtainConfig).hide_lightsensor && this.LightSensor?.Service) { + const set_minLux = (this.device as curtainConfig).set_minLux ?? 1 + const set_maxLux = (this.device as curtainConfig).set_maxLux ?? 6001 const lightLevel = this.deviceStatus.lightLevel === 'bright' ? set_maxLux : set_minLux this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 2) await this.debugLog(`CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`) @@ -480,14 +478,14 @@ export class Curtain extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as curtainServiceData | curtain3ServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as curtainServiceData | curtain3ServiceData // Update HomeKit if ((serviceData.model === SwitchBotBLEModel.Curtain || SwitchBotBLEModel.Curtain3) && (serviceData.modelName === SwitchBotBLEModelName.Curtain || SwitchBotBLEModelName.Curtain3)) { @@ -495,8 +493,8 @@ export class Curtain extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -505,17 +503,17 @@ export class Curtain extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -533,7 +531,7 @@ export class Curtain extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -556,7 +554,7 @@ export class Curtain extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -593,7 +591,7 @@ export class Curtain extends deviceBase { async BLEpushChanges(): Promise { await this.debugLog('BLEpushChanges') if (this.WindowCovering.TargetPosition !== this.WindowCovering.CurrentPosition) { - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId @@ -601,14 +599,15 @@ export class Curtain extends deviceBase { const { setPositionMode, Mode }: { setPositionMode: number, Mode: string } = await this.setPerformance() const adjustedMode = setPositionMode === 1 ? 0x01 : 0xFF await this.debugLog(`Mode: ${Mode}, setPositionMode: ${setPositionMode}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, quick: true, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: SwitchbotDevice[]) => { + const deviceList = device_list as unknown as WoCurtain[] return await this.retryBLE({ max: await this.maxRetryBLE(), fn: async () => { - return await device_list[0].runToPos(100 - Number(this.WindowCovering.TargetPosition), adjustedMode) + return await deviceList[0].runToPos(100 - Number(this.WindowCovering.TargetPosition), adjustedMode) }, }) }) @@ -622,7 +621,7 @@ export class Curtain extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -641,30 +640,29 @@ export class Curtain extends deviceBase { const { setPositionMode, Mode }: { setPositionMode: number, Mode: string } = await this.setPerformance() await this.debugLog(`Mode: ${Mode}, setPositionMode: ${setPositionMode}`) const adjustedMode = setPositionMode || 'ff' - let bodyChange: string + let bodyChange: bodyChange if (this.WindowCovering.HoldPosition) { - bodyChange = JSON.stringify({ + bodyChange = { command: 'pause', parameter: 'default', commandType: 'command', - }) + } } else { - bodyChange = JSON.stringify({ + bodyChange = { command: 'setPosition', parameter: `0,${adjustedMode},${adjustedTargetPosition}`, commandType: 'command', - }) + } } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -742,7 +740,7 @@ export class Curtain extends deviceBase { * Handle requests to set the value of the "Target Position" characteristic */ async OpenModeSwitchSet(value: CharacteristicValue): Promise { - if (this.OpenModeSwitch && this.device.curtain?.silentModeSwitch) { + if (this.OpenModeSwitch && (this.device as curtainConfig).silentModeSwitch) { this.debugLog(`Silent Open Mode: ${value}`) this.OpenModeSwitch.On = value this.accessory.context.OpenModeSwitch.On = value @@ -756,7 +754,7 @@ export class Curtain extends deviceBase { * Handle requests to set the value of the "Target Position" characteristic */ async CloseModeSwitchSet(value: CharacteristicValue): Promise { - if (this.CloseModeSwitch && this.device.curtain?.silentModeSwitch) { + if (this.CloseModeSwitch && (this.device as curtainConfig).silentModeSwitch) { this.debugLog(`Silent Close Mode: ${value}`) this.CloseModeSwitch.On = value this.accessory.context.CloseModeSwitch.On = value @@ -777,7 +775,7 @@ export class Curtain extends deviceBase { // HoldPosition await this.updateCharacteristic(this.WindowCovering.Service, this.hap.Characteristic.HoldPosition, this.WindowCovering.HoldPosition, 'HoldPosition') // CurrentAmbientLightLevel - if (!this.device.curtain?.hide_lightsensor && this.LightSensor?.Service) { + if (!(this.device as curtainConfig).hide_lightsensor && this.LightSensor?.Service) { const history = { time: Math.round(new Date().valueOf() / 1000), lux: this.LightSensor.CurrentAmbientLightLevel } await this.updateCharacteristic(this.LightSensor?.Service, this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor?.CurrentAmbientLightLevel, 'CurrentAmbientLightLevel', history) } @@ -808,10 +806,10 @@ export class Curtain extends deviceBase { let setPositionMode: number let Mode: string if (Number(this.WindowCovering.TargetPosition) > 50) { - if (this.device.curtain?.setOpenMode === '1' || this.OpenModeSwitch?.On) { + if ((this.device as curtainConfig).setOpenMode === '1' || this.OpenModeSwitch?.On) { setPositionMode = 1 Mode = 'Silent Mode' - } else if (this.device.curtain?.setOpenMode === '0' || !this.OpenModeSwitch?.On) { + } else if ((this.device as curtainConfig).setOpenMode === '0' || !this.OpenModeSwitch?.On) { setPositionMode = 0 Mode = 'Performance Mode' } else { @@ -819,10 +817,10 @@ export class Curtain extends deviceBase { Mode = 'Default Mode' } } else { - if (this.device.curtain?.setCloseMode === '1' || this.CloseModeSwitch?.On) { + if ((this.device as curtainConfig).setCloseMode === '1' || this.CloseModeSwitch?.On) { setPositionMode = 1 Mode = 'Silent Mode' - } else if (this.device.curtain?.setCloseMode === '0' || !this.CloseModeSwitch?.On) { + } else if ((this.device as curtainConfig).setCloseMode === '0' || !this.CloseModeSwitch?.On) { setPositionMode = 0 Mode = 'Performance Mode' } else { @@ -838,7 +836,7 @@ export class Curtain extends deviceBase { await this.setMinMax() await this.debugLog(`CurrentPosition ${this.WindowCovering.CurrentPosition}`) this.hasLoggedStandby = this.hasLoggedStandby ?? false - if (this.setNewTarget || this.deviceStatus.moving) { + if (this.deviceStatus ? (this.setNewTarget || this.deviceStatus.moving) : this.setNewTarget) { this.hasLoggedStandby = false this.infoLog('Checking Status ...') this.curtainMoving = true @@ -874,13 +872,13 @@ export class Curtain extends deviceBase { } async setMinMax(): Promise { - if (this.device.curtain?.set_min) { - if (Number(this.WindowCovering.CurrentPosition) <= this.device.curtain?.set_min) { + if ((this.device as curtainConfig).set_min) { + if (Number(this.WindowCovering.CurrentPosition) <= (this.device as curtainConfig).set_min!) { this.WindowCovering.CurrentPosition = 0 } } - if (this.device.curtain?.set_max) { - if (Number(this.WindowCovering.CurrentPosition) >= this.device.curtain?.set_max) { + if ((this.device as curtainConfig).set_max) { + if (Number(this.WindowCovering.CurrentPosition) >= (this.device as curtainConfig).set_max!) { this.WindowCovering.CurrentPosition = 100 } } @@ -906,7 +904,7 @@ export class Curtain extends deviceBase { this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e) this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e) this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, e) - if (!this.device.curtain?.hide_lightsensor && this.LightSensor?.Service) { + if (!(this.device as curtainConfig).hide_lightsensor && this.LightSensor?.Service) { this.LightSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e) this.LightSensor.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e) } diff --git a/src/device/device.ts b/src/device/device.ts index d1700476..856e56eb 100644 --- a/src/device/device.ts +++ b/src/device/device.ts @@ -5,19 +5,16 @@ import type { API, CharacteristicValue, HAP, Logging, PlatformAccessory, Service } from 'homebridge' import type { MqttClient } from 'mqtt' +import type { ad, bodyChange, device, deviceStatus, deviceStatusRequest, pushResponse } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig, SwitchBotPlatformConfig } from '../settings.js' -import type { ad } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' +import type { blindTiltConfig, botConfig, ceilingLightConfig, colorBulbConfig, contactConfig, curtainConfig, devicesConfig, hubConfig, humidifierConfig, indoorOutdoorSensorConfig, lockConfig, meterConfig, motionConfig, plugConfig, stripLightConfig, SwitchBotPlatformConfig, waterDetectorConfig } from '../settings.js' import { hostname } from 'node:os' import { SwitchBotBLEModel, SwitchBotBLEModelFriendlyName, SwitchBotBLEModelName, SwitchBotModel } from 'node-switchbot' -import { request } from 'undici' -import { Devices } from '../settings.js' -import { BlindTiltMappingMode, formatDeviceIdAsMac, sleep } from '../utils.js' +import { formatDeviceIdAsMac, sleep } from '../utils.js' export abstract class deviceBase { public readonly api: API @@ -182,25 +179,65 @@ export abstract class deviceBase { device.maxRetries !== 0 && { maxRetries: device.maxRetries }, device.delayBetweenRetries !== 0 && { delayBetweenRetries: device.delayBetweenRetries }, ) + let deviceSpecificConfig = {} + switch (device.configDeviceType) { + case 'Bot': + deviceSpecificConfig = device as botConfig + break + case 'Meter': + case 'MeterPlus': + deviceSpecificConfig = device as meterConfig + break + case 'WoIOSensor': + deviceSpecificConfig = device as indoorOutdoorSensorConfig + break + case 'Humidifier': + deviceSpecificConfig = device as humidifierConfig + break + case 'Curtain': + case 'Curtain3': + deviceSpecificConfig = device as curtainConfig + break + case 'Blind Tilt': + deviceSpecificConfig = device as blindTiltConfig + break + case 'Contact Sensor': + deviceSpecificConfig = device as contactConfig + break + case 'Motion Sensor': + deviceSpecificConfig = device as motionConfig + break + case 'Water Detector': + deviceSpecificConfig = device as waterDetectorConfig + break + case 'Plug': + case 'Plug Mini (US)': + case 'Plug Mini (JP)': + deviceSpecificConfig = device as plugConfig + break + case 'Color Bulb': + deviceSpecificConfig = device as colorBulbConfig + break + case 'Strip Light': + deviceSpecificConfig = device as stripLightConfig + break + case 'Ceiling Light': + case 'Ceiling Light Pro': + deviceSpecificConfig = device as ceilingLightConfig + break + case 'Smart Lock': + case 'Smart Lock Pro': + deviceSpecificConfig = device as lockConfig + break + case 'Hub 2': + deviceSpecificConfig = device as hubConfig + break + default: + } const config = Object.assign( {}, deviceConfig, - device.bot, - device.lock, - device.ceilinglight, - device.colorbulb, - device.contact, - device.motion, - device.curtain, - device.hub, - device.waterdetector, - device.humidifier, - device.meter, - device.iosensor, - device.striplight, - device.plug, - device.blindTilt?.mode === undefined ? { mode: BlindTiltMappingMode.OnlyUp } : {}, - device.blindTilt, + deviceSpecificConfig, ) if (Object.keys(config).length !== 0) { @@ -278,14 +315,14 @@ export abstract class deviceBase { } async switchbotBLE(): Promise { - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) // Convert to BLE Address try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - await this.getCustomBLEAddress(switchbot) - return switchbot + await this.getCustomBLEAddress(switchBotBLE) + return switchBotBLE } catch (error) { await this.errorLog(`failed to format device ID as MAC, Error: ${error}`) } @@ -293,7 +330,11 @@ export abstract class deviceBase { async monitorAdvertisementPackets(switchbot: any) { await this.debugLog(`Scanning for ${this.device.bleModelName} devices...`) - await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }) + try { + await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac }) + } catch (e: any) { + await this.errorLog(`Failed to start BLE scanning. Error:${e.message ?? e}`) + } // Set an event handler let serviceData = { model: this.device.bleModel, modelName: this.device.bleModelName } as ad['serviceData'] switchbot.onadvertisement = async (ad: ad) => { @@ -303,14 +344,18 @@ export abstract class deviceBase { this.debugLog(`serviceData: ${JSON.stringify(ad.serviceData)}`) serviceData = ad.serviceData } else { - serviceData = { model: '', modelName: '' } as ad['serviceData'] + serviceData = { model: '', modelName: '', modelFriendlyName: '' } as unknown as ad['serviceData'] this.debugLog(`serviceData: ${JSON.stringify(ad.serviceData)}`) } } // Wait await switchbot.wait(this.scanDuration * 1000) // Stop to monitor - await switchbot.stopScan() + try { + await switchbot.stopScan() + } catch (e: any) { + await this.errorLog(`Failed to stop BLE scanning. Error:${e.message ?? e}`) + } return serviceData } @@ -319,32 +364,38 @@ export abstract class deviceBase { this.debugLog(`customBLEaddress: ${this.device.customBLEaddress}`); (async () => { // Start to monitor advertisement packets - await switchbot.startScan({ model: this.device.bleModel }) + try { + await switchbot.startScan({ model: this.device.bleModel }) + } catch (e: any) { + await this.errorLog(`Failed to start BLE scanning. Error:${e.message ?? e}`) + } // Set an event handler switchbot.onadvertisement = async (ad: ad) => { this.warnLog(`ad: ${JSON.stringify(ad, null, ' ')}`) } await sleep(10000) // Stop to monitor - switchbot.stopScan() + try { + switchbot.stopScan() + } catch (e: any) { + await this.errorLog(`Failed to stop BLE scanning. Error:${e.message ?? e}`) + } })() } } - async pushChangeRequest(bodyChange: string): Promise<{ body: any, statusCode: any }> { - return await request(`${Devices}/${this.device.deviceId}/commands`, { - body: bodyChange, - method: 'POST', - headers: this.platform.generateHeaders(), - }) + async pushChangeRequest(bodyChange: bodyChange): Promise<{ body: pushResponse['body'], statusCode: pushResponse['statusCode'] }> { + const { response, statusCode } = await this.platform.switchBotAPI.controlDevice(this.device.deviceId, bodyChange.command, bodyChange.parameter, bodyChange.commandType) + return { body: response, statusCode } } - async deviceRefreshStatus(): Promise<{ body: any, statusCode: any }> { - return await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries, `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() }) + async deviceRefreshStatus(): Promise<{ body: deviceStatus, statusCode: deviceStatusRequest['statusCode'] }> { + const { response, statusCode } = await this.platform.retryRequest(this.device.deviceId, this.deviceMaxRetries, this.deviceDelayBetweenRetries) + return { body: response, statusCode } } - async successfulStatusCodes(statusCode: any, deviceStatus: any) { - return (statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100) + async successfulStatusCodes(deviceStatus: deviceStatusRequest) { + return (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100) } /** @@ -466,6 +517,18 @@ export abstract class deviceBase { bleModelName: SwitchBotBLEModelName.Curtain3, bleModelFriendlyName: SwitchBotBLEModelFriendlyName.Curtain3, }, + 'WoRollerShade': { + model: SwitchBotModel.Curtain3, + bleModel: SwitchBotBLEModel.Curtain3, + bleModelName: SwitchBotBLEModelName.Curtain3, + bleModelFriendlyName: SwitchBotBLEModelFriendlyName.Curtain3, + }, + 'Roller Shade': { + model: SwitchBotModel.Curtain3, + bleModel: SwitchBotBLEModel.Curtain3, + bleModelName: SwitchBotBLEModelName.Curtain3, + bleModelFriendlyName: SwitchBotBLEModelFriendlyName.Curtain3, + }, 'Blind Tilt': { model: SwitchBotModel.BlindTilt, bleModel: SwitchBotBLEModel.BlindTilt, @@ -514,6 +577,12 @@ export abstract class deviceBase { bleModelName: SwitchBotBLEModelName.Unknown, bleModelFriendlyName: SwitchBotBLEModelFriendlyName.Unknown, }, + 'K10+ Pro': { + model: SwitchBotModel.K10Pro, + bleModel: SwitchBotBLEModel.Unknown, + bleModelName: SwitchBotBLEModelName.Unknown, + bleModelFriendlyName: SwitchBotBLEModelFriendlyName.Unknown, + }, 'WoSweeper': { model: SwitchBotModel.WoSweeper, bleModel: SwitchBotBLEModel.Unknown, diff --git a/src/device/fan.ts b/src/device/fan.ts index db59f68b..fcf9cc76 100644 --- a/src/device/fan.ts +++ b/src/device/fan.ts @@ -3,13 +3,10 @@ * plug.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { batteryCirculatorFanServiceData, batteryCirculatorFanStatus, batteryCirculatorFanWebhookContext, bodyChange, device, SwitchbotDevice } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' import type { devicesConfig } from '../settings.js' -import type { batteryCirculatorFanServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { batteryCirculatorFanStatus } from '../types/devicestatus.js' -import type { batteryCirculatorFanWebhookContext } from '../types/devicewebhookstatus.js' /* * For Testing Locally: @@ -149,7 +146,7 @@ export class Fan extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -157,7 +154,7 @@ export class Fan extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -165,7 +162,7 @@ export class Fan extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -204,7 +201,8 @@ export class Fan extends deviceBase { await this.debugLog(`Active: ${this.Fan.Active}`) // RotationSpeed - this.Fan.RotationSpeed = this.serviceData.fanSpeed + this.Fan.RotationSpeed = this.serviceData.fanSpeed // ?? 0 + await this.debugLog(`RotationSpeed: ${this.Fan.RotationSpeed}`) } async openAPIparseStatus() { @@ -222,7 +220,7 @@ export class Fan extends deviceBase { await this.debugLog(`SwingMode: ${this.Fan.SwingMode}`) // RotationSpeed - this.Fan.RotationSpeed = this.deviceStatus.fanSpeed + this.Fan.RotationSpeed = this.deviceStatus.fanSpeed ?? 0 await this.debugLog(`RotationSpeed: ${this.Fan.RotationSpeed}`) // ChargingState @@ -271,7 +269,7 @@ export class Fan extends deviceBase { await this.debugLog(`SwingMode: ${this.Fan.SwingMode}`) // RotationSpeed - this.Fan.RotationSpeed = this.webhookContext.fanSpeed + this.Fan.RotationSpeed = this.webhookContext.fanSpeed // ?? 0 await this.debugLog(`RotationSpeed: ${this.Fan.RotationSpeed}`) // BatteryLevel @@ -322,22 +320,22 @@ export class Fan extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as batteryCirculatorFanServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as batteryCirculatorFanServiceData // Update HomeKit if (serviceData.model === SwitchBotBLEModel.Unknown && SwitchBotBLEModelName.Unknown) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -358,7 +356,7 @@ export class Fan extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -372,17 +370,17 @@ export class Fan extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -400,7 +398,7 @@ export class Fan extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -453,22 +451,23 @@ export class Fan extends deviceBase { await this.debugLog('BLEpushChanges') if (this.Fan.Active !== this.accessory.context.Active) { await this.debugLog(`BLEpushChanges On: ${this.Fan.Active} OnCached: ${this.accessory.context.Active}`) - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: SwitchbotDevice[]) => { + const deviceList = device_list as unknown as SwitchbotDevice[] return await this.retryBLE({ max: await this.maxRetryBLE(), fn: async () => { if (this.Fan.Active) { - return await device_list[0].turnOn({ id: this.device.bleMac }) + return await deviceList[0].turnOn() } else { - return await device_list[0].turnOff({ id: this.device.bleMac }) + return await deviceList[0].turnOff() } }, }) @@ -483,7 +482,7 @@ export class Fan extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -498,21 +497,20 @@ export class Fan extends deviceBase { await this.debugLog('openAPIpushChanges') if (this.Fan.Active !== this.accessory.context.Active) { const command = this.Fan.Active ? 'turnOn' : 'turnOff' - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: `${command}`, parameter: 'default', commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -527,21 +525,20 @@ export class Fan extends deviceBase { async pushRotationSpeedChanges(): Promise { await this.debugLog('pushRotationSpeedChanges') if (this.Fan.SwingMode !== this.accessory.context.SwingMode) { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'setWindSpeed', parameter: `${this.Fan.RotationSpeed}`, commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -557,21 +554,20 @@ export class Fan extends deviceBase { await this.debugLog('pushSwingModeChanges') if (this.Fan.SwingMode !== this.accessory.context.SwingMode) { const parameter = this.Fan.SwingMode === this.hap.Characteristic.SwingMode.SWING_ENABLED ? 'on' : 'off' - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'setOscillation', parameter: `${parameter}`, commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { diff --git a/src/device/hub.ts b/src/device/hub.ts index 3fac786a..16da3aa4 100644 --- a/src/device/hub.ts +++ b/src/device/hub.ts @@ -3,13 +3,10 @@ * hub.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { device, hub2ServiceData, hub2Status, hub2WebhookContext } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { hub2ServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { hub2Status } from '../types/devicestatus.js' -import type { hub2WebhookContext } from '../types/devicewebhookstatus.js' +import type { devicesConfig, hubConfig } from '../settings.js' import { Units } from 'homebridge' /* @@ -69,7 +66,7 @@ export class Hub extends deviceBase { this.hubUpdateInProgress = false // Initialize Temperature Sensor Service - if (device.hub?.hide_temperature) { + if ((device as hubConfig).hide_temperature) { if (this.TemperatureSensor) { this.debugLog('Removing Temperature Sensor Service') this.TemperatureSensor.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service @@ -97,7 +94,7 @@ export class Hub extends deviceBase { } // Initialize Humidity Sensor Service - if (device.hub?.hide_humidity) { + if ((device as hubConfig).hide_humidity) { if (this.HumiditySensor) { this.debugLog('Removing Humidity Sensor Service') this.HumiditySensor.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service @@ -121,7 +118,7 @@ export class Hub extends deviceBase { } // Initialize Light Sensor Service - if (device.hub?.hide_lightsensor) { + if ((device as hubConfig).hide_lightsensor) { if (this.LightSensor) { this.debugLog('Removing Light Sensor Service') this.LightSensor.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service @@ -149,7 +146,7 @@ export class Hub extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -157,7 +154,7 @@ export class Hub extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -165,7 +162,7 @@ export class Hub extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -181,21 +178,21 @@ export class Hub extends deviceBase { await this.debugLog(`(temperature, humidity, lightLevel) = BLE:(${this.serviceData.celsius}, ${this.serviceData.humidity}, ${this.serviceData.lightLevel}), current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity}, ${this.LightSensor?.CurrentAmbientLightLevel})`) // CurrentTemperature - if (!this.device.hub?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as hubConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.CurrentTemperature = this.serviceData.celsius await this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}°c`) } // CurrentRelativeHumidity - if (!this.device.hub?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as hubConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor!.CurrentRelativeHumidity = validHumidity(this.serviceData.humidity, 0, 100) await this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}%`) } // CurrentAmbientLightLevel - if (!this.device.hub?.hide_lightsensor && this.LightSensor?.Service) { - const set_minLux = this.device.blindTilt?.set_minLux ?? 1 - const set_maxLux = this.device.blindTilt?.set_maxLux ?? 6001 + if (!(this.device as hubConfig).hide_lightsensor && this.LightSensor?.Service) { + const set_minLux = (this.device as hubConfig).set_minLux ?? 1 + const set_maxLux = (this.device as hubConfig).set_maxLux ?? 6001 const lightLevel = this.serviceData.lightLevel this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 19) await this.debugLog(`LightLevel: ${this.serviceData.lightLevel}, CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`) @@ -207,21 +204,21 @@ export class Hub extends deviceBase { await this.debugLog(`(temperature, humidity, lightLevel) = OpenAPI:(${this.deviceStatus.temperature}, ${this.deviceStatus.humidity}, ${this.deviceStatus.lightLevel}), current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity}, ${this.LightSensor?.CurrentAmbientLightLevel})`) // CurrentRelativeHumidity - if (!this.device.hub?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as hubConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.CurrentRelativeHumidity = this.deviceStatus.humidity await this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}%`) } // CurrentTemperature - if (!this.device.hub?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as hubConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.CurrentTemperature = this.deviceStatus.temperature await this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}°c`) } // LightSensor - if (!this.device.hub?.hide_lightsensor && this.LightSensor?.Service) { - const set_minLux = this.device.blindTilt?.set_minLux ?? 1 - const set_maxLux = this.device.blindTilt?.set_maxLux ?? 6001 + if (!(this.device as hubConfig).hide_lightsensor && this.LightSensor?.Service) { + const set_minLux = (this.device as hubConfig).set_minLux ?? 1 + const set_maxLux = (this.device as hubConfig).set_maxLux ?? 6001 const lightLevel = this.deviceStatus.lightLevel this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 19) await this.debugLog(`LightLevel: ${this.deviceStatus.lightLevel}, CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`) @@ -245,28 +242,28 @@ export class Hub extends deviceBase { async parseStatusWebhook(): Promise { await this.debugLog('parseStatusWebhook') - await this.debugLog(`(scale, temperature, humidity, lightLevel) = Webhook:(${this.webhookContext.scale}, ${convertUnits(this.webhookContext.temperature, this.webhookContext.scale, this.device.hub?.convertUnitTo)}, ${this.webhookContext.humidity}, ${this.webhookContext.lightLevel}), current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity}, ${this.LightSensor?.CurrentAmbientLightLevel})`) + await this.debugLog(`(scale, temperature, humidity, lightLevel) = Webhook:(${this.webhookContext.scale}, ${convertUnits(this.webhookContext.temperature, this.webhookContext.scale, (this.device as hubConfig).convertUnitTo)}, ${this.webhookContext.humidity}, ${this.webhookContext.lightLevel}), current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity}, ${this.LightSensor?.CurrentAmbientLightLevel})`) // Check if the scale is not CELSIUS - if (this.webhookContext.scale !== 'CELSIUS' && this.device.hub?.convertUnitTo === undefined) { + if (this.webhookContext.scale !== 'CELSIUS' && (this.device as hubConfig).convertUnitTo === undefined) { await this.warnLog(`received a non-CELSIUS Webhook scale: ${this.webhookContext.scale}, Use the *convertUnitsTo* config under Hub settings, if displaying incorrectly in HomeKit.`) } // CurrentRelativeHumidity - if (!this.device.hub?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as hubConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.CurrentRelativeHumidity = this.webhookContext.humidity await this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}`) } // CurrentTemperature - if (!this.device.hub?.hide_temperature && this.TemperatureSensor?.Service) { - this.TemperatureSensor.CurrentTemperature = convertUnits(this.webhookContext.temperature, this.webhookContext.scale, this.device.hub?.convertUnitTo) + if (!(this.device as hubConfig).hide_temperature && this.TemperatureSensor?.Service) { + this.TemperatureSensor.CurrentTemperature = convertUnits(this.webhookContext.temperature, this.webhookContext.scale, (this.device as hubConfig).convertUnitTo) await this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}`) } // CurrentAmbientLightLevel - if (!this.device.hub?.hide_lightsensor && this.LightSensor?.Service) { - const set_minLux = this.device.blindTilt?.set_minLux ?? 1 - const set_maxLux = this.device.blindTilt?.set_maxLux ?? 6001 + if (!(this.device as hubConfig).hide_lightsensor && this.LightSensor?.Service) { + const set_minLux = (this.device as hubConfig).set_minLux ?? 1 + const set_maxLux = (this.device as hubConfig).set_maxLux ?? 6001 this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(this.webhookContext.lightLevel, set_minLux, set_maxLux, 19) await this.debugLog(`CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`) } @@ -290,22 +287,22 @@ export class Hub extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as hub2ServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as hub2ServiceData // Update HomeKit if (serviceData.model === SwitchBotBLEModel.Hub2 && serviceData.modelName === SwitchBotBLEModelName.Hub2) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -326,7 +323,7 @@ export class Hub extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -340,17 +337,17 @@ export class Hub extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -368,7 +365,7 @@ export class Hub extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -382,15 +379,15 @@ export class Hub extends deviceBase { async updateHomeKitCharacteristics(): Promise { // CurrentRelativeHumidity - if (!this.device.hub?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as hubConfig).hide_humidity && this.HumiditySensor?.Service) { await this.updateCharacteristic(this.HumiditySensor.Service, this.hap.Characteristic.CurrentRelativeHumidity, this.HumiditySensor.CurrentRelativeHumidity, 'CurrentRelativeHumidity') } // CurrentTemperature - if (!this.device.hub?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as hubConfig).hide_temperature && this.TemperatureSensor?.Service) { await this.updateCharacteristic(this.TemperatureSensor.Service, this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor.CurrentTemperature, 'CurrentTemperature') } // CurrentAmbientLightLevel - if (!this.device.hub?.hide_lightsensor && this.LightSensor?.Service) { + if (!(this.device as hubConfig).hide_lightsensor && this.LightSensor?.Service) { await this.updateCharacteristic(this.LightSensor.Service, this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor.CurrentAmbientLightLevel, 'CurrentAmbientLightLevel') } } @@ -405,26 +402,26 @@ export class Hub extends deviceBase { async offlineOff(): Promise { if (this.device.offline) { - if (!this.device.hub?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as hubConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.accessory.context.CurrentTemperature) } - if (!this.device.hub?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as hubConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.accessory.context.CurrentRelativeHumidity) } - if (!this.device.hub?.hide_lightsensor && this.LightSensor?.Service) { + if (!(this.device as hubConfig).hide_lightsensor && this.LightSensor?.Service) { this.LightSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.accessory.context.CurrentAmbientLightLevel) } } } async apiError(e: any): Promise { - if (!this.device.hub?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as hubConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e) } - if (!this.device.hub?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as hubConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e) } - if (!this.device.hub?.hide_lightsensor && this.LightSensor?.Service) { + if (!(this.device as hubConfig).hide_lightsensor && this.LightSensor?.Service) { this.LightSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e) } } diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts index 3f19b611..403ea2c0 100644 --- a/src/device/humidifier.ts +++ b/src/device/humidifier.ts @@ -3,13 +3,10 @@ * humidifier.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, device, humidifierServiceData, humidifierStatus, humidifierWebhookContext, SwitchbotDevice, WoHumi } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { humidifierServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { humidifierStatus } from '../types/devicestatus.js' -import type { humidifierWebhookContext } from '../types/devicewebhookstatus.js' +import type { devicesConfig, humidifierConfig } from '../settings.js' /* * For Testing Locally: @@ -103,13 +100,13 @@ export class Humidifier extends deviceBase { validValueRanges: [0, 100], minValue: 0, maxValue: 100, - minStep: device.humidifier?.set_minStep ?? 1, + minStep: (device as humidifierConfig).set_minStep ?? 1, }).onGet(() => { return this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold }).onSet(this.RelativeHumidityHumidifierThresholdSet.bind(this)) // Initialize the Temperature Sensor Service - if (device.humidifier?.hide_temperature) { + if ((device as humidifierConfig).hide_temperature) { if (this.TemperatureSensor) { this.debugLog('Removing Temperature Sensor Service') this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service @@ -140,7 +137,7 @@ export class Humidifier extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -148,7 +145,7 @@ export class Humidifier extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -156,7 +153,7 @@ export class Humidifier extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -236,7 +233,7 @@ export class Humidifier extends deviceBase { await this.debugLog(`CurrentRelativeHumidity: ${this.HumidifierDehumidifier.CurrentRelativeHumidity}`) // Current Temperature - if (!this.device.humidifier?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as humidifierConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.CurrentTemperature = this.deviceStatus.temperature await this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}`) } @@ -289,15 +286,15 @@ export class Humidifier extends deviceBase { async parseStatusWebhook(): Promise { await this.debugLog('parseStatusWebhook') - await this.debugLog(`(temperature, humidity) = Webhook:(${convertUnits(this.webhookContext.temperature, this.webhookContext.scale, this.device.iosensor?.convertUnitTo)}, ${this.webhookContext.humidity}), current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumidifierDehumidifier.CurrentRelativeHumidity})`) + await this.debugLog(`(temperature, humidity) = Webhook:(${convertUnits(this.webhookContext.temperature, this.webhookContext.scale, (this.device as humidifierConfig).convertUnitTo)}, ${this.webhookContext.humidity}), current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumidifierDehumidifier.CurrentRelativeHumidity})`) // CurrentRelativeHumidity this.HumidifierDehumidifier.CurrentRelativeHumidity = validHumidity(this.webhookContext.humidity) await this.debugLog(`CurrentRelativeHumidity: ${this.HumidifierDehumidifier.CurrentRelativeHumidity}`) // CurrentTemperature - if (!this.device.humidifier?.hide_temperature && this.TemperatureSensor?.Service) { - this.TemperatureSensor.CurrentTemperature = convertUnits(this.webhookContext.temperature, this.webhookContext.scale, this.device.iosensor?.convertUnitTo) + if (!(this.device as humidifierConfig).hide_temperature && this.TemperatureSensor?.Service) { + this.TemperatureSensor.CurrentTemperature = convertUnits(this.webhookContext.temperature, this.webhookContext.scale, (this.device as humidifierConfig).convertUnitTo) await this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}`) } } @@ -320,22 +317,22 @@ export class Humidifier extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as humidifierServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as humidifierServiceData // Update HomeKit if (serviceData.model === SwitchBotBLEModel.Humidifier && serviceData.modelName === SwitchBotBLEModelName.Humidifier) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -356,7 +353,7 @@ export class Humidifier extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -370,17 +367,17 @@ export class Humidifier extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -398,7 +395,7 @@ export class Humidifier extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } @@ -432,16 +429,17 @@ export class Humidifier extends deviceBase { if ((this.HumidifierDehumidifier.TargetHumidifierDehumidifierState === this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER) && (this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.ACTIVE) && (this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold !== this.HumidifierDehumidifier.CurrentRelativeHumidity)) { - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, quick: true, id: this.device.bleMac }) - .then(async (device_list: any) => { - return await device_list[0].percentage(this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold) + .then(async (device_list: SwitchbotDevice[]) => { + const deviceList = device_list as unknown as WoHumi[] + return await deviceList[0].percentage(this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold) }) .then(async () => { await this.successLog(`RelativeHumidityHumidifierThreshold: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold} sent over BLE, sent successfully`) @@ -453,7 +451,7 @@ export class Humidifier extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -470,21 +468,20 @@ export class Humidifier extends deviceBase { && (this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.ACTIVE) && (this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold !== this.HumidifierDehumidifier.CurrentRelativeHumidity)) { await this.debugLog(`Auto Off, RelativeHumidityHumidifierThreshold: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}!`) - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'setMode', parameter: `${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}`, commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -507,21 +504,20 @@ export class Humidifier extends deviceBase { === this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER) && (this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.ACTIVE)) { await this.debugLog('Pushing Auto') - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'setMode', parameter: 'auto', commandType: 'command', - }) + } await this.debugLog(`pushAutoChanges, SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -540,21 +536,20 @@ export class Humidifier extends deviceBase { await this.debugLog('pushActiveChanges') if (this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.INACTIVE) { await this.debugLog('Pushing Off') - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'turnOff', parameter: 'default', commandType: 'command', - }) + } await this.debugLog(`pushActiveChanges, SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -630,7 +625,7 @@ export class Humidifier extends deviceBase { // RelativeHumidityHumidifierThreshold await this.updateCharacteristic(this.HumidifierDehumidifier.Service, this.hap.Characteristic.RelativeHumidityHumidifierThreshold, this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold, 'RelativeHumidityHumidifierThreshold') // CurrentTemperature - if (!this.device.humidifier?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as humidifierConfig).hide_temperature && this.TemperatureSensor?.Service) { await this.updateCharacteristic(this.TemperatureSensor.Service, this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor.CurrentTemperature, 'CurrentTemperature') } } @@ -665,7 +660,7 @@ export class Humidifier extends deviceBase { this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.TargetHumidifierDehumidifierState, e) this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.Active, e) this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.RelativeHumidityHumidifierThreshold, e) - if (!this.device.humidifier?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as humidifierConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e) } } diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts index 163b5e87..d0cc42d9 100644 --- a/src/device/iosensor.ts +++ b/src/device/iosensor.ts @@ -3,13 +3,10 @@ * iosensor.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { device, outdoorMeterServiceData, outdoorMeterStatus, outdoorMeterWebhookContext } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { outdoorMeterServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { outdoorMeterStatus } from '../types/devicestatus.js' -import type { outdoorMeterWebhookContext } from '../types/devicewebhookstatus.js' +import type { devicesConfig, indoorOutdoorSensorConfig } from '../settings.js' import { Units } from 'homebridge' /* @@ -95,7 +92,7 @@ export class IOSensor extends deviceBase { accessory.context.BatteryName = this.Battery.Name // InitializeTemperature Sensor Service - if (device.iosensor?.hide_temperature) { + if ((device as indoorOutdoorSensorConfig).hide_temperature) { if (this.TemperatureSensor) { this.debugLog('Removing Temperature Sensor Service') this.TemperatureSensor.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service @@ -123,7 +120,7 @@ export class IOSensor extends deviceBase { } // Initialize Humidity Sensor Service - if (device.iosensor?.hide_humidity) { + if ((device as indoorOutdoorSensorConfig).hide_humidity) { if (this.HumiditySensor) { this.debugLog('Removing Humidity Sensor Service') this.HumiditySensor.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service @@ -151,7 +148,7 @@ export class IOSensor extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -159,7 +156,7 @@ export class IOSensor extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -167,7 +164,7 @@ export class IOSensor extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -193,13 +190,13 @@ export class IOSensor extends deviceBase { await this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`) // CurrentRelativeHumidity - if (!this.device.iosensor?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as indoorOutdoorSensorConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.CurrentRelativeHumidity = validHumidity(this.serviceData.humidity, 0, 100) await this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}%`) } // Current Temperature - if (!this.device.meter?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as indoorOutdoorSensorConfig).hide_temperature && this.TemperatureSensor?.Service) { const CELSIUS = this.serviceData.celsius < 0 ? 0 : this.serviceData.celsius > 100 ? 100 : this.serviceData.celsius this.TemperatureSensor.CurrentTemperature = CELSIUS await this.debugLog(`Temperature: ${this.TemperatureSensor.CurrentTemperature}°c`) @@ -221,13 +218,13 @@ export class IOSensor extends deviceBase { await this.debugLog(`StatusLowBattery: ${this.Battery.StatusLowBattery}`) // CurrentRelativeHumidity - if (!this.device.iosensor?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as indoorOutdoorSensorConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.CurrentRelativeHumidity = this.deviceStatus.humidity await this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}%`) } // Current Temperature - if (!this.device.meter?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as indoorOutdoorSensorConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.CurrentTemperature = this.deviceStatus.temperature await this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}°c`) } @@ -250,19 +247,19 @@ export class IOSensor extends deviceBase { async parseStatusWebhook(): Promise { await this.debugLog('parseStatusWebhook') - await this.debugLog(`(scale, temperature, humidity) = Webhook:(${this.webhookContext.scale}, ${convertUnits(this.webhookContext.temperature, this.webhookContext.scale, this.device.iosensor?.convertUnitTo)}, ${this.webhookContext.humidity}), current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity})`) + await this.debugLog(`(scale, temperature, humidity) = Webhook:(${this.webhookContext.scale}, ${convertUnits(this.webhookContext.temperature, this.webhookContext.scale, (this.device as indoorOutdoorSensorConfig).convertUnitTo)}, ${this.webhookContext.humidity}), current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity})`) - if (this.webhookContext.scale !== 'CELSIUS' && this.device.iosensor?.convertUnitTo === undefined) { + if (this.webhookContext.scale !== 'CELSIUS' && (this.device as indoorOutdoorSensorConfig).convertUnitTo === undefined) { await this.warnLog(`received a non-CELSIUS Webhook scale: ${this.webhookContext.scale}, Use the *convertUnitsTo* config under Hub settings, if displaying incorrectly in HomeKit.`) } // CurrentRelativeHumidity - if (this.device.iosensor?.hide_humidity && this.HumiditySensor?.Service) { + if ((this.device as indoorOutdoorSensorConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.CurrentRelativeHumidity = this.webhookContext.humidity await this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}%`) } // CurrentTemperature - if (this.device.iosensor?.hide_temperature && this.TemperatureSensor?.Service) { - this.TemperatureSensor.CurrentTemperature = convertUnits(this.webhookContext.temperature, this.webhookContext.scale, this.device.iosensor?.convertUnitTo) + if ((this.device as indoorOutdoorSensorConfig).hide_temperature && this.TemperatureSensor?.Service) { + this.TemperatureSensor.CurrentTemperature = convertUnits(this.webhookContext.temperature, this.webhookContext.scale, (this.device as indoorOutdoorSensorConfig).convertUnitTo) await this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}°c`) } } @@ -285,22 +282,22 @@ export class IOSensor extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as outdoorMeterServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as outdoorMeterServiceData // Update HomeKit if (serviceData.model === SwitchBotBLEModel.OutdoorMeter && serviceData.modelName === SwitchBotBLEModelName.OutdoorMeter) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -321,7 +318,7 @@ export class IOSensor extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -335,17 +332,17 @@ export class IOSensor extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -363,7 +360,7 @@ export class IOSensor extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -376,11 +373,11 @@ export class IOSensor extends deviceBase { */ async updateHomeKitCharacteristics(): Promise { // CurrentRelativeHumidity - if (!this.device.iosensor?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as indoorOutdoorSensorConfig).hide_humidity && this.HumiditySensor?.Service) { await this.updateCharacteristic(this.HumiditySensor.Service, this.hap.Characteristic.CurrentRelativeHumidity, this.HumiditySensor.CurrentRelativeHumidity, 'CurrentRelativeHumidity') } // CurrentTemperature - if (!this.device.iosensor?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as indoorOutdoorSensorConfig).hide_temperature && this.TemperatureSensor?.Service) { await this.updateCharacteristic(this.TemperatureSensor.Service, this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor.CurrentTemperature, 'CurrentTemperature') } // BatteryLevel @@ -399,20 +396,20 @@ export class IOSensor extends deviceBase { async offlineOff(): Promise { if (this.device.offline) { - if (!this.device.iosensor?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as indoorOutdoorSensorConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, 50) } - if (!this.device.iosensor?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as indoorOutdoorSensorConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, 30) } } } async apiError(e: any): Promise { - if (!this.device.iosensor?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as indoorOutdoorSensorConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e) } - if (!this.device.iosensor?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as indoorOutdoorSensorConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e) } this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e) diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts index b284ac8c..3b36182d 100644 --- a/src/device/lightstrip.ts +++ b/src/device/lightstrip.ts @@ -3,13 +3,10 @@ * lightstrip.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, Controller, ControllerConstructor, ControllerServiceMap, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, device, stripLightServiceData, stripLightStatus, stripLightWebhookContext, SwitchbotDevice, WoStrip } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { stripLightServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { stripLightStatus } from '../types/devicestatus.js' -import type { stripLightWebhookContext } from '../types/devicewebhookstatus.js' +import type { devicesConfig, stripLightConfig } from '../settings.js' /* * For Testing Locally: @@ -111,7 +108,7 @@ export class StripLight extends deviceBase { // Initialize LightBulb Brightness Characteristic this.LightBulb.Service.getCharacteristic(this.hap.Characteristic.Brightness).setProps({ - minStep: device.striplight?.set_minStep ?? 1, + minStep: (device as stripLightConfig).set_minStep ?? 1, minValue: 0, maxValue: 100, validValueRanges: [0, 100], @@ -151,7 +148,7 @@ export class StripLight extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -159,7 +156,7 @@ export class StripLight extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -167,7 +164,7 @@ export class StripLight extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -312,22 +309,22 @@ export class StripLight extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as stripLightServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as stripLightServiceData // Update HomeKit if (serviceData.model === SwitchBotBLEModel.StripLight && serviceData.modelName === SwitchBotBLEModelName.StripLight) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -348,7 +345,7 @@ export class StripLight extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -362,17 +359,17 @@ export class StripLight extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -390,7 +387,7 @@ export class StripLight extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -459,23 +456,24 @@ export class StripLight extends deviceBase { await this.debugLog('BLEpushChanges') if (this.LightBulb.On !== this.accessory.context.On) { await this.debugLog(`BLEpushChanges On: ${this.LightBulb.On}, OnCached: ${this.accessory.context.On}`) - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: SwitchbotDevice[]) => { + const deviceList = device_list as unknown as WoStrip[] await this.infoLog(`On: ${this.LightBulb.On}`) return await this.retryBLE({ max: await this.maxRetryBLE(), fn: async () => { if (this.LightBulb.On) { - return await device_list[0].turnOn({ id: this.device.bleMac }) + return await deviceList[0].turnOn() } else { - return await device_list[0].turnOff({ id: this.device.bleMac }) + return await deviceList[0].turnOff() } }, }) @@ -490,7 +488,7 @@ export class StripLight extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -504,15 +502,15 @@ export class StripLight extends deviceBase { async BLEpushBrightnessChanges(): Promise { await this.debugLog('BLEpushBrightnessChanges') if (this.LightBulb.Brightness !== this.accessory.context.Brightness) { - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: SwitchbotDevice[]) => { await this.infoLog(`Brightness: ${this.LightBulb.Brightness}`) return await device_list[0].setBrightness(this.LightBulb.Brightness) }) @@ -526,7 +524,7 @@ export class StripLight extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -543,15 +541,15 @@ export class StripLight extends deviceBase { await this.debugLog(`Hue: ${JSON.stringify(this.LightBulb.Hue)}, Saturation: ${JSON.stringify(this.LightBulb.Saturation)}`) const [red, green, blue] = hs2rgb(this.LightBulb.Hue, this.LightBulb.Saturation) await this.debugLog(`rgb: ${JSON.stringify([red, green, blue])}`) - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: SwitchbotDevice[]) => { await this.infoLog(`RGB: ${(this.LightBulb.Brightness, red, green, blue)}`) return await device_list[0].setRGB(this.LightBulb.Brightness, red, green, blue) }) @@ -565,7 +563,7 @@ export class StripLight extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -580,21 +578,20 @@ export class StripLight extends deviceBase { await this.debugLog('openAPIpushChanges') if (this.LightBulb.On !== this.accessory.context.On) { const command = this.LightBulb.On ? 'turnOn' : 'turnOff' - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: `${command}`, parameter: 'default', commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -612,21 +609,20 @@ export class StripLight extends deviceBase { await this.debugLog(`Hue: ${JSON.stringify(this.LightBulb.Hue)}, Saturation: ${JSON.stringify(this.LightBulb.Saturation)}`) const [red, green, blue] = hs2rgb(this.LightBulb.Hue, this.LightBulb.Saturation) await this.debugLog(`rgb: ${JSON.stringify([red, green, blue])}`) - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'setColor', parameter: `${red}:${green}:${blue}`, commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -641,21 +637,20 @@ export class StripLight extends deviceBase { async pushBrightnessChanges(): Promise { await this.debugLog('pushBrightnessChanges') if (this.LightBulb.Brightness !== this.accessory.context.Brightness) { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'setBrightness', parameter: `${this.LightBulb.Brightness}`, commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -791,13 +786,8 @@ export class StripLight extends deviceBase { this.adaptiveLighting = accessory.context.adaptiveLighting ?? true await this.debugLog(`adaptiveLighting: ${this.adaptiveLighting}`) // Adaptive Lighting Shift - if (device.striplight?.adaptiveLightingShift) { - this.adaptiveLightingShift = device.striplight.adaptiveLightingShift - this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`) - } else { - this.adaptiveLightingShift = 0 - this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`) - } + this.adaptiveLightingShift = (device as stripLightConfig).adaptiveLightingShift ?? 0 + this.debugLog(`adaptiveLightingShift: ${this.adaptiveLightingShift}`) } async BLEPushConnection() { diff --git a/src/device/lock.ts b/src/device/lock.ts index a5bfce30..37f60e0f 100644 --- a/src/device/lock.ts +++ b/src/device/lock.ts @@ -3,13 +3,10 @@ * lock.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, device, lockProServiceData, lockProStatus, lockProWebhookContext, lockServiceData, lockStatus, lockWebhookContext, SwitchbotDevice } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { lockProServiceData, lockServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { lockProStatus, lockStatus } from '../types/devicestatus.js' -import type { lockProWebhookContext, lockWebhookContext } from '../types/devicewebhookstatus.js' +import type { devicesConfig, lockConfig } from '../settings.js' /* * For Testing Locally: @@ -110,7 +107,7 @@ export class Lock extends deviceBase { }) // Contact Sensor Service - if (device.lock?.hide_contactsensor) { + if ((device as lockConfig).hide_contactsensor) { if (this.ContactSensor) { this.debugLog('Removing Contact Sensor Service') this.ContactSensor.Service = this.accessory.getService(this.hap.Service.ContactSensor) as Service @@ -132,7 +129,7 @@ export class Lock extends deviceBase { } // Initialize Latch Button Service - if (device.lock?.activate_latchbutton === false) { + if ((device as lockConfig).activate_latchbutton === false) { if (this.Switch) { this.debugLog('Removing Latch Button Service') this.Switch.Service = accessory.getService(this.hap.Service.Switch) as Service @@ -158,7 +155,7 @@ export class Lock extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -166,7 +163,7 @@ export class Lock extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -174,7 +171,7 @@ export class Lock extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -221,8 +218,8 @@ export class Lock extends deviceBase { await this.debugLog(`LockTargetState: ${this.LockMechanism.LockTargetState}`) // Contact Sensor - if (!this.device.lock?.hide_contactsensor && this.ContactSensor?.Service) { - this.ContactSensor.ContactSensorState = this.serviceData.door_open === 'opened' + if (!(this.device as lockConfig).hide_contactsensor && this.ContactSensor?.Service) { + this.ContactSensor.ContactSensorState = this.serviceData.door_open ? this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED : this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED await this.debugLog(`ContactSensorState: ${this.ContactSensor.ContactSensorState}`) @@ -256,7 +253,7 @@ export class Lock extends deviceBase { await this.debugLog(`LockTargetState: ${this.LockMechanism.LockTargetState}`) // ContactSensorState - if (!this.device.lock?.hide_contactsensor && this.ContactSensor?.Service) { + if (!(this.device as lockConfig).hide_contactsensor && this.ContactSensor?.Service) { this.ContactSensor.ContactSensorState = this.deviceStatus.doorState === 'opened' ? this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED : this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED @@ -324,14 +321,14 @@ export class Lock extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as lockServiceData | lockProServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as lockServiceData | lockProServiceData // Update HomeKit if ((serviceData.model === SwitchBotBLEModel.Lock || SwitchBotBLEModel.LockPro) && (serviceData.modelName === SwitchBotBLEModelName.Lock || SwitchBotBLEModelName.LockPro)) { @@ -339,8 +336,8 @@ export class Lock extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -361,7 +358,7 @@ export class Lock extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -375,17 +372,17 @@ export class Lock extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -403,7 +400,7 @@ export class Lock extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -440,15 +437,15 @@ export class Lock extends deviceBase { async BLEpushChanges(): Promise { await this.debugLog('BLEpushChanges') if (this.LockMechanism.LockTargetState !== this.accessory.context.LockTargetState) { - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: SwitchbotDevice[]) => { return await this.retryBLE({ max: await this.maxRetryBLE(), fn: async () => { @@ -470,7 +467,7 @@ export class Lock extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -486,21 +483,20 @@ export class Lock extends deviceBase { if ((this.LockMechanism.LockTargetState !== this.accessory.context.LockTargetState) || LatchUnlock) { // Determine the command based on the LockTargetState or the forceUnlock parameter const command = LatchUnlock ? 'unlock' : this.LockMechanism.LockTargetState ? 'lock' : 'unlock' - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: `${command}`, parameter: 'default', commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -549,7 +545,7 @@ export class Lock extends deviceBase { } }).catch(async (e: any) => { // Log the error if the operation failed - await this.debugLog(`Error opening latch: ${e}`) + await this.debugLog(`Error opening latch: ${e.message ?? e}`) // Ensure we turn the switch back off even in case of an error if (this.Switch?.Service) { this.Switch.Service.getCharacteristic(this.hap.Characteristic.On).updateValue(false) @@ -570,7 +566,7 @@ export class Lock extends deviceBase { // LockCurrentState await this.updateCharacteristic(this.LockMechanism.Service, this.hap.Characteristic.LockCurrentState, this.LockMechanism.LockCurrentState, 'LockCurrentState') // ContactSensorState - if (!this.device.lock?.hide_contactsensor && this.ContactSensor?.Service) { + if (!(this.device as lockConfig).hide_contactsensor && this.ContactSensor?.Service) { await this.updateCharacteristic(this.ContactSensor.Service, this.hap.Characteristic.ContactSensorState, this.ContactSensor.ContactSensorState, 'ContactSensorState') } // BatteryLevel @@ -598,7 +594,7 @@ export class Lock extends deviceBase { if (this.device.offline) { this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED) this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, this.hap.Characteristic.LockCurrentState.SECURED) - if (!this.device.lock?.hide_contactsensor && this.ContactSensor?.Service) { + if (!(this.device as lockConfig).hide_contactsensor && this.ContactSensor?.Service) { this.ContactSensor.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED) } } @@ -607,7 +603,7 @@ export class Lock extends deviceBase { async apiError(e: any): Promise { this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockTargetState, e) this.LockMechanism.Service.updateCharacteristic(this.hap.Characteristic.LockCurrentState, e) - if (!this.device.lock?.hide_contactsensor && this.ContactSensor?.Service) { + if (!(this.device as lockConfig).hide_contactsensor && this.ContactSensor?.Service) { this.ContactSensor.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, e) } this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e) diff --git a/src/device/meter.ts b/src/device/meter.ts index d6e354b0..5dcc92e8 100644 --- a/src/device/meter.ts +++ b/src/device/meter.ts @@ -3,13 +3,10 @@ * meter.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { device, meterServiceData, meterStatus, meterWebhookContext } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { meterServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { meterStatus } from '../types/devicestatus.js' -import type { meterWebhookContext } from '../types/devicewebhookstatus.js' +import type { devicesConfig, meterConfig } from '../settings.js' import { Units } from 'homebridge' /* @@ -89,7 +86,7 @@ export class Meter extends deviceBase { }) // Initialize Temperature Sensor Service - if (device.meter?.hide_temperature) { + if ((device as meterConfig).hide_temperature) { if (this.TemperatureSensor) { this.debugLog('Removing Temperature Sensor Service') this.TemperatureSensor.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service @@ -116,7 +113,7 @@ export class Meter extends deviceBase { }) } // Initialize Humidity Sensor Service - if (device.meter?.hide_humidity) { + if ((device as meterConfig).hide_humidity) { if (this.HumiditySensor) { this.debugLog('Removing Humidity Sensor Service') this.HumiditySensor.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service @@ -144,7 +141,7 @@ export class Meter extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -152,7 +149,7 @@ export class Meter extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -160,7 +157,7 @@ export class Meter extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -176,13 +173,13 @@ export class Meter extends deviceBase { await this.debugLog(`(scale, temperature, humidity) = BLE:(${this.serviceData.fahrenheit}, ${this.serviceData.celsius}, ${this.serviceData.humidity}), current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity})`) // CurrentRelativeHumidity - if (!this.device.iosensor?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as meterConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.CurrentRelativeHumidity = validHumidity(this.serviceData.humidity, 0, 100) await this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}%`) } // Current Temperature - if (!this.device.meter?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as meterConfig).hide_temperature && this.TemperatureSensor?.Service) { const CELSIUS = this.serviceData.celsius < 0 ? 0 : this.serviceData.celsius > 100 ? 100 : this.serviceData.celsius this.TemperatureSensor.CurrentTemperature = CELSIUS await this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}°c`) @@ -204,13 +201,13 @@ export class Meter extends deviceBase { await this.debugLog(`(battery, temperature, humidity) = OpenAPI:(${this.deviceStatus.battery}, ${this.deviceStatus.temperature}, ${this.deviceStatus.humidity}), current:(${this.Battery?.BatteryLevel}, ${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity})`) // CurrentRelativeHumidity - if (!this.device.meter?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as meterConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.CurrentRelativeHumidity = this.deviceStatus.humidity await this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}%`) } // CurrentTemperature - if (!this.device.meter?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as meterConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.CurrentTemperature = this.deviceStatus.temperature await this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}°c`) } @@ -243,21 +240,21 @@ export class Meter extends deviceBase { async parseStatusWebhook(): Promise { await this.debugLog('parseStatusWebhook') - await this.debugLog(`(scale, temperature, humidity) = Webhook:(${this.webhookContext.scale}, ${convertUnits(this.webhookContext.temperature, this.webhookContext.scale, this.device.meter?.convertUnitTo)}, ${this.webhookContext.humidity}), current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity})`) + await this.debugLog(`(scale, temperature, humidity) = Webhook:(${this.webhookContext.scale}, ${convertUnits(this.webhookContext.temperature, this.webhookContext.scale, (this.device as meterConfig).convertUnitTo)}, ${this.webhookContext.humidity}), current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity})`) // Check if the scale is not CELSIUS - if (this.webhookContext.scale !== 'CELSIUS' && this.device.hub?.convertUnitTo === undefined) { + if (this.webhookContext.scale !== 'CELSIUS' && (this.device as meterConfig).convertUnitTo === undefined) { await this.warnLog(`received a non-CELSIUS Webhook scale: ${this.webhookContext.scale}, Use the *convertUnitsTo* config under Hub settings, if displaying incorrectly in HomeKit.`) } // CurrentRelativeHumidity - if (!this.device.meter?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as meterConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.CurrentRelativeHumidity = this.webhookContext.humidity await this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}`) } // CurrentTemperature - if (!this.device.meter?.hide_temperature && this.TemperatureSensor?.Service) { - this.TemperatureSensor.CurrentTemperature = convertUnits(this.webhookContext.temperature, this.webhookContext.scale, this.device.hub?.convertUnitTo) + if (!(this.device as meterConfig).hide_temperature && this.TemperatureSensor?.Service) { + this.TemperatureSensor.CurrentTemperature = convertUnits(this.webhookContext.temperature, this.webhookContext.scale, (this.device as meterConfig).convertUnitTo) await this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}`) } } @@ -280,22 +277,22 @@ export class Meter extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as meterServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as meterServiceData // Update HomeKit if (serviceData.model === SwitchBotBLEModel.Meter && serviceData.modelName === SwitchBotBLEModelName.Meter) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -316,7 +313,7 @@ export class Meter extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -330,17 +327,17 @@ export class Meter extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -358,7 +355,7 @@ export class Meter extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -371,11 +368,11 @@ export class Meter extends deviceBase { */ async updateHomeKitCharacteristics(): Promise { // CurrentRelativeHumidity - if (!this.device.meter?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as meterConfig).hide_humidity && this.HumiditySensor?.Service) { await this.updateCharacteristic(this.HumiditySensor.Service, this.hap.Characteristic.CurrentRelativeHumidity, this.HumiditySensor.CurrentRelativeHumidity, 'CurrentRelativeHumidity') } // CurrentTemperature - if (!this.device.meter?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as meterConfig).hide_temperature && this.TemperatureSensor?.Service) { await this.updateCharacteristic(this.TemperatureSensor.Service, this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor.CurrentTemperature, 'CurrentTemperature') } // BatteryLevel @@ -394,10 +391,10 @@ export class Meter extends deviceBase { async offlineOff(): Promise { if (this.device.offline) { - if (!this.device.meter?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as meterConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, 50) } - if (!this.device.meter?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as meterConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, 30) } this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, 100) @@ -406,10 +403,10 @@ export class Meter extends deviceBase { } async apiError(e: any): Promise { - if (!this.device.meter?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as meterConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e) } - if (!this.device.meter?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as meterConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e) } this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e) diff --git a/src/device/meterplus.ts b/src/device/meterplus.ts index 2e81ca0d..a4132716 100644 --- a/src/device/meterplus.ts +++ b/src/device/meterplus.ts @@ -3,13 +3,10 @@ * meterplus.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { device, meterPlusServiceData, meterPlusStatus, meterPlusWebhookContext } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { meterPlusServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { meterPlusStatus } from '../types/devicestatus.js' -import type { meterPlusWebhookContext } from '../types/devicewebhookstatus.js' +import type { devicesConfig, meterConfig } from '../settings.js' import { Units } from 'homebridge' /* @@ -94,7 +91,7 @@ export class MeterPlus extends deviceBase { }) // Initialize Temperature Sensor Service - if (device.meter?.hide_temperature) { + if ((device as meterConfig).hide_temperature) { if (this.TemperatureSensor) { this.debugLog('Removing Temperature Sensor Service') this.TemperatureSensor.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service @@ -121,7 +118,7 @@ export class MeterPlus extends deviceBase { }) } // Initialize Humidity Sensor Service - if (device.meter?.hide_humidity) { + if ((device as meterConfig).hide_humidity) { if (this.HumiditySensor) { this.debugLog('Removing Humidity Sensor Service') this.HumiditySensor.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service @@ -149,7 +146,7 @@ export class MeterPlus extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -157,7 +154,7 @@ export class MeterPlus extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -165,7 +162,7 @@ export class MeterPlus extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -181,12 +178,12 @@ export class MeterPlus extends deviceBase { await this.debugLog(`(temperature, humidity) = BLE:(${this.serviceData.celsius}, ${this.serviceData.humidity}), current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity})`) // CurrentRelativeHumidity - if (!this.device.iosensor?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as meterConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.CurrentRelativeHumidity = validHumidity(this.serviceData.humidity, 0, 100) await this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}%`) } // Current Temperature - if (!this.device.meter?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as meterConfig).hide_temperature && this.TemperatureSensor?.Service) { const CELSIUS = this.serviceData.celsius < 0 ? 0 : this.serviceData.celsius > 100 ? 100 : this.serviceData.celsius this.TemperatureSensor.CurrentTemperature = CELSIUS await this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}°c`) @@ -206,13 +203,13 @@ export class MeterPlus extends deviceBase { await this.debugLog(`(temperature, humidity) = OpenAPI:(${this.deviceStatus.temperature}, ${this.deviceStatus.humidity}), current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity})`) // CurrentRelativeHumidity - if (!this.device.meter?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as meterConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.CurrentRelativeHumidity = this.deviceStatus.humidity await this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}%`) } // CurrentTemperature - if (!this.device.meter?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as meterConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.CurrentTemperature = this.deviceStatus.temperature await this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}°c`) } @@ -245,21 +242,21 @@ export class MeterPlus extends deviceBase { async parseStatusWebhook(): Promise { await this.debugLog('parseStatusWebhook') - await this.debugLog(`(scale, temperature, humidity) = Webhook:(${this.webhookContext.scale}, ${convertUnits(this.webhookContext.temperature, this.webhookContext.scale, this.device.meter?.convertUnitTo)}, ${this.webhookContext.humidity}) current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity})`) + await this.debugLog(`(scale, temperature, humidity) = Webhook:(${this.webhookContext.scale}, ${convertUnits(this.webhookContext.temperature, this.webhookContext.scale, (this.device as meterConfig).convertUnitTo)}, ${this.webhookContext.humidity}) current:(${this.TemperatureSensor?.CurrentTemperature}, ${this.HumiditySensor?.CurrentRelativeHumidity})`) // Check if the scale is not CELSIUS - if (this.webhookContext.scale !== 'CELSIUS' && this.device.hub?.convertUnitTo === undefined) { + if (this.webhookContext.scale !== 'CELSIUS' && (this.device as meterConfig).convertUnitTo === undefined) { await this.warnLog(`received a non-CELSIUS Webhook scale: ${this.webhookContext.scale}, Use the *convertUnitsTo* config under Hub settings, if displaying incorrectly in HomeKit.`) } // CurrentRelativeHumidity - if (!this.device.meter?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as meterConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.CurrentRelativeHumidity = this.webhookContext.humidity await this.debugLog(`CurrentRelativeHumidity: ${this.HumiditySensor.CurrentRelativeHumidity}`) } // CurrentTemperature - if (!this.device.meter?.hide_temperature && this.TemperatureSensor?.Service) { - this.TemperatureSensor.CurrentTemperature = convertUnits(this.webhookContext.temperature, this.webhookContext.scale, this.device.hub?.convertUnitTo) + if (!(this.device as meterConfig).hide_temperature && this.TemperatureSensor?.Service) { + this.TemperatureSensor.CurrentTemperature = convertUnits(this.webhookContext.temperature, this.webhookContext.scale, (this.device as meterConfig).convertUnitTo) await this.debugLog(`CurrentTemperature: ${this.TemperatureSensor.CurrentTemperature}`) } } @@ -282,22 +279,22 @@ export class MeterPlus extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as meterPlusServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as meterPlusServiceData // Update HomeKit if (serviceData.model === SwitchBotBLEModel.MeterPlus && serviceData.modelName === SwitchBotBLEModelName.MeterPlus) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -318,7 +315,7 @@ export class MeterPlus extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -332,17 +329,17 @@ export class MeterPlus extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -360,7 +357,7 @@ export class MeterPlus extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -373,11 +370,11 @@ export class MeterPlus extends deviceBase { */ async updateHomeKitCharacteristics(): Promise { // CurrentRelativeHumidity - if (!this.device.meter?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as meterConfig).hide_humidity && this.HumiditySensor?.Service) { await this.updateCharacteristic(this.HumiditySensor.Service, this.hap.Characteristic.CurrentRelativeHumidity, this.HumiditySensor.CurrentRelativeHumidity, 'CurrentRelativeHumidity') } // CurrentTemperature - if (!this.device.meter?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as meterConfig).hide_temperature && this.TemperatureSensor?.Service) { await this.updateCharacteristic(this.TemperatureSensor.Service, this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor.CurrentTemperature, 'CurrentTemperature') } // BatteryLevel @@ -396,10 +393,10 @@ export class MeterPlus extends deviceBase { async offlineOff(): Promise { if (this.device.offline) { - if (!this.device.meter?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as meterConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, 50) } - if (!this.device.meter?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as meterConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, 30) } this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, 100) @@ -408,10 +405,10 @@ export class MeterPlus extends deviceBase { } async apiError(e: any): Promise { - if (!this.device.meter?.hide_humidity && this.HumiditySensor?.Service) { + if (!(this.device as meterConfig).hide_humidity && this.HumiditySensor?.Service) { this.HumiditySensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e) } - if (!this.device.meter?.hide_temperature && this.TemperatureSensor?.Service) { + if (!(this.device as meterConfig).hide_temperature && this.TemperatureSensor?.Service) { this.TemperatureSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e) } this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e) diff --git a/src/device/motion.ts b/src/device/motion.ts index 6bc53b3e..ae8900da 100644 --- a/src/device/motion.ts +++ b/src/device/motion.ts @@ -3,13 +3,10 @@ * motion.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { device, motionSensorServiceData, motionSensorStatus, motionSensorWebhookContext } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { motionSensorServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { motionSensorStatus } from '../types/devicestatus.js' -import type { motionSensorWebhookContext } from '../types/devicewebhookstatus.js' +import type { devicesConfig, motionConfig } from '../settings.js' /* * For Testing Locally: @@ -107,7 +104,7 @@ export class Motion extends deviceBase { }) // Initialize Light Sensor Service - if (device.motion?.hide_lightsensor) { + if ((device as motionConfig).hide_lightsensor) { if (this.LightSensor) { this.debugLog('Removing Light Sensor Service') this.LightSensor.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service @@ -133,7 +130,7 @@ export class Motion extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -141,7 +138,7 @@ export class Motion extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -149,7 +146,7 @@ export class Motion extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -169,9 +166,9 @@ export class Motion extends deviceBase { await this.debugLog(`MotionDetected: ${this.MotionSensor.MotionDetected}`) // CurrentAmbientLightLevel - if (!this.device.motion?.hide_lightsensor && this.LightSensor?.Service) { - const set_minLux = this.device.blindTilt?.set_minLux ?? 1 - const set_maxLux = this.device.blindTilt?.set_maxLux ?? 6001 + if (!(this.device as motionConfig).hide_lightsensor && this.LightSensor?.Service) { + const set_minLux = (this.device as motionConfig).set_minLux ?? 1 + const set_maxLux = (this.device as motionConfig).set_maxLux ?? 6001 const lightLevel = this.serviceData.lightLevel === 'bright' ? set_maxLux : set_minLux this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 2) await this.debugLog(`LightLevel: ${this.serviceData.lightLevel}, CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`) @@ -197,9 +194,9 @@ export class Motion extends deviceBase { await this.debugLog(`MotionDetected: ${this.MotionSensor.MotionDetected}`) // CurrentAmbientLightLevel - if (!this.device.motion?.hide_lightsensor && this.LightSensor?.Service) { - const set_minLux = this.device.blindTilt?.set_minLux ?? 1 - const set_maxLux = this.device.blindTilt?.set_maxLux ?? 6001 + if (!(this.device as motionConfig).hide_lightsensor && this.LightSensor?.Service) { + const set_minLux = (this.device as motionConfig).set_minLux ?? 1 + const set_maxLux = (this.device as motionConfig).set_maxLux ?? 6001 const lightLevel = this.deviceStatus.brightness === 'bright' ? set_maxLux : set_minLux this.LightSensor.CurrentAmbientLightLevel = await this.getLightLevel(lightLevel, set_minLux, set_maxLux, 2) await this.debugLog(`LightLevel: ${this.deviceStatus.brightness}, CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`) @@ -257,22 +254,22 @@ export class Motion extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as motionSensorServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as motionSensorServiceData // Update HomeKit if (serviceData.model === SwitchBotBLEModel.MotionSensor && serviceData.modelName === SwitchBotBLEModelName.MotionSensor) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -293,7 +290,7 @@ export class Motion extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -307,17 +304,17 @@ export class Motion extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -335,7 +332,7 @@ export class Motion extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -354,7 +351,7 @@ export class Motion extends deviceBase { // StatusLowBattery await this.updateCharacteristic(this.Battery.Service, this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery, 'StatusLowBattery') // CurrentAmbientLightLevel - if (!this.device.motion?.hide_lightsensor && this.LightSensor?.Service) { + if (!(this.device as motionConfig).hide_lightsensor && this.LightSensor?.Service) { await this.updateCharacteristic(this.LightSensor.Service, this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor.CurrentAmbientLightLevel, 'CurrentAmbientLightLevel') } } @@ -377,7 +374,7 @@ export class Motion extends deviceBase { this.MotionSensor.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, e) this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e) this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e) - if (!this.device.motion?.hide_lightsensor && this.LightSensor?.Service) { + if (!(this.device as motionConfig).hide_lightsensor && this.LightSensor?.Service) { this.LightSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e) } } diff --git a/src/device/plug.ts b/src/device/plug.ts index 576745f8..24f505f9 100644 --- a/src/device/plug.ts +++ b/src/device/plug.ts @@ -3,13 +3,10 @@ * plug.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, device, plugMiniJPServiceData, plugMiniJPWebhookContext, plugMiniStatus, plugMiniUSServiceData, plugMiniUSWebhookContext, plugStatus, plugWebhookContext, SwitchbotDevice, WoPlugMini } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' import type { devicesConfig } from '../settings.js' -import type { plugMiniJPServiceData, plugMiniUSServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { plugMiniStatus, plugStatus } from '../types/devicestatus.js' -import type { plugMiniJPWebhookContext, plugMiniUSWebhookContext, plugWebhookContext } from '../types/devicewebhookstatus.js' /* * For Testing Locally: @@ -74,7 +71,7 @@ export class Plug extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -82,7 +79,7 @@ export class Plug extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -90,7 +87,7 @@ export class Plug extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -180,14 +177,14 @@ export class Plug extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as plugMiniUSServiceData | plugMiniJPServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as plugMiniUSServiceData | plugMiniJPServiceData // Update HomeKit if ((serviceData.model === SwitchBotBLEModel.PlugMiniUS || SwitchBotBLEModel.PlugMiniJP) && serviceData.modelName === (SwitchBotBLEModelName.PlugMini || SwitchBotBLEModelName.PlugMini)) { @@ -195,8 +192,8 @@ export class Plug extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -217,7 +214,7 @@ export class Plug extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -231,17 +228,17 @@ export class Plug extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -259,7 +256,7 @@ export class Plug extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -301,23 +298,24 @@ export class Plug extends deviceBase { await this.debugLog('BLEpushChanges') if (this.Outlet.On !== this.accessory.context.On) { await this.debugLog(`BLEpushChanges On: ${this.Outlet.On}, OnCached: ${this.accessory.context.On}`) - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: SwitchbotDevice[]) => { + const deviceList = device_list as unknown as WoPlugMini[] await this.infoLog(`On: ${this.Outlet.On}`) return await this.retryBLE({ max: await this.maxRetryBLE(), fn: async () => { if (this.Outlet.On) { - return await device_list[0].turnOn({ id: this.device.bleMac }) + return await deviceList[0].turnOn() } else { - return await device_list[0].turnOff({ id: this.device.bleMac }) + return await deviceList[0].turnOff() } }, }) @@ -332,7 +330,7 @@ export class Plug extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -347,21 +345,20 @@ export class Plug extends deviceBase { await this.debugLog('openAPIpushChanges') if (this.Outlet.On !== this.accessory.context.On) { const command = this.Outlet.On ? 'turnOn' : 'turnOff' - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: `${command}`, parameter: 'default', commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { diff --git a/src/device/robotvacuumcleaner.ts b/src/device/robotvacuumcleaner.ts index 4cec871c..9937acf5 100644 --- a/src/device/robotvacuumcleaner.ts +++ b/src/device/robotvacuumcleaner.ts @@ -3,17 +3,10 @@ * robotvacuumcleaner.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, device, floorCleaningRobotS10Status, floorCleaningRobotS10WebhookContext, robotVacuumCleanerS1PlusStatus, robotVacuumCleanerS1PlusWebhookContext, robotVacuumCleanerS1Status, robotVacuumCleanerS1WebhookContext, robotVacuumCleanerServiceData, SwitchbotDevice } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' import type { devicesConfig } from '../settings.js' -import type { robotVacuumCleanerServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { floorCleaningRobotS10Status, robotVacuumCleanerS1PlusStatus, robotVacuumCleanerS1Status } from '../types/devicestatus.js' -import type { - floorCleaningRobotS10WebhookContext, - robotVacuumCleanerS1PlusWebhookContext, - robotVacuumCleanerS1WebhookContext, -} from '../types/devicewebhookstatus.js' /* * For Testing Locally: @@ -123,7 +116,7 @@ export class RobotVacuumCleaner extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -131,7 +124,7 @@ export class RobotVacuumCleaner extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -139,7 +132,7 @@ export class RobotVacuumCleaner extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -271,22 +264,22 @@ export class RobotVacuumCleaner extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as unknown as robotVacuumCleanerServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as unknown as robotVacuumCleanerServiceData // Update HomeKit if (serviceData.model === SwitchBotBLEModel.Unknown && serviceData.modelName === SwitchBotBLEModelName.Unknown) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -307,7 +300,7 @@ export class RobotVacuumCleaner extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -321,17 +314,17 @@ export class RobotVacuumCleaner extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -349,7 +342,7 @@ export class RobotVacuumCleaner extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -388,23 +381,24 @@ export class RobotVacuumCleaner extends deviceBase { async BLEpushChanges(): Promise { await this.debugLog('BLEpushChanges') if (this.LightBulb.On !== this.accessory.context.On) { - const switchbot = await this.platform.connectBLE(this.accessory, this.device) + const switchBotBLE = await this.platform.connectBLE(this.accessory, this.device) try { const formattedDeviceId = formatDeviceIdAsMac(this.device.deviceId) this.device.bleMac = formattedDeviceId await this.debugLog(`bleMac: ${this.device.bleMac}`) - if (switchbot !== false) { - switchbot + if (switchBotBLE !== false) { + switchBotBLE .discover({ model: this.device.bleModel, id: this.device.bleMac }) - .then(async (device_list: any) => { + .then(async (device_list: SwitchbotDevice[]) => { + const deviceList = device_list as unknown as SwitchbotDevice[] await this.infoLog(`On: ${this.LightBulb.On}`) return await this.retryBLE({ max: await this.maxRetryBLE(), fn: async () => { if (this.LightBulb.On) { - return await device_list[0].turnOn({ id: this.device.bleMac }) + return await deviceList[0].turnOn() } else { - return await device_list[0].turnOff({ id: this.device.bleMac }) + return await deviceList[0].turnOff() } }, }) @@ -419,7 +413,7 @@ export class RobotVacuumCleaner extends deviceBase { await this.BLEPushConnection() }) } else { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(switchBotBLE)}`) await this.BLEPushConnection() } } catch (error) { @@ -434,21 +428,20 @@ export class RobotVacuumCleaner extends deviceBase { await this.debugLog('openAPIpushChanges') if (this.LightBulb.On !== this.accessory.context.On) { const command = this.LightBulb.On ? 'start' : 'dock' - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: `${command}`, parameter: 'default', commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -471,21 +464,20 @@ export class RobotVacuumCleaner extends deviceBase { : this.LightBulb.Brightness === 75 ? '2' : this.LightBulb.Brightness === 100 ? '3' : 'default' - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: `${command}`, parameter: `${parameter}`, commandType: 'command', - }) + } await this.debugLog(`SwitchBot OpenAPI bodyChange: ${JSON.stringify(bodyChange)}`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { diff --git a/src/device/waterdetector.ts b/src/device/waterdetector.ts index de9f32e1..08fbbffb 100644 --- a/src/device/waterdetector.ts +++ b/src/device/waterdetector.ts @@ -3,13 +3,10 @@ * waterdetector.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { device, waterLeakDetectorServiceData, waterLeakDetectorStatus, waterLeakDetectorWebhookContext } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { devicesConfig } from '../settings.js' -import type { waterLeakDetectorServiceData } from '../types/bledevicestatus.js' -import type { device } from '../types/devicelist.js' -import type { waterLeakDetectorStatus } from '../types/devicestatus.js' -import type { waterLeakDetectorWebhookContext } from '../types/devicewebhookstatus.js' +import type { devicesConfig, waterDetectorConfig } from '../settings.js' /* * For Testing Locally: @@ -90,7 +87,7 @@ export class WaterDetector extends deviceBase { }) // Initialize Leak Sensor Service - if (device.waterdetector?.hide_leak) { + if ((device as waterDetectorConfig).hide_leak) { if (this.LeakSensor) { this.debugLog('Removing Leak Sensor Service') this.LeakSensor.Service = this.accessory.getService(this.hap.Service.LeakSensor) as Service @@ -119,7 +116,7 @@ export class WaterDetector extends deviceBase { this.debugLog('Retrieve initial values and update Homekit') this.refreshStatus() } catch (e: any) { - this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e}`) + this.errorLog(`failed to retrieve initial values and update Homekit, Error: ${e.message ?? e}`) } // regisiter webhook event handler if enabled @@ -127,7 +124,7 @@ export class WaterDetector extends deviceBase { this.debugLog('Registering Webhook Event Handler') this.registerWebhook() } catch (e: any) { - this.errorLog(`failed to registerWebhook, Error: ${e}`) + this.errorLog(`failed to registerWebhook, Error: ${e.message ?? e}`) } // regisiter platform BLE event handler if enabled @@ -135,7 +132,7 @@ export class WaterDetector extends deviceBase { this.debugLog('Registering Platform BLE Event Handler') this.registerPlatformBLE() } catch (e: any) { - this.errorLog(`failed to registerPlatformBLE, Error: ${e}`) + this.errorLog(`failed to registerPlatformBLE, Error: ${e.message ?? e}`) } // Start an update interval @@ -152,13 +149,13 @@ export class WaterDetector extends deviceBase { await this.debugLog(`(state, status, battery) = BLE: (${this.serviceData.state}, ${this.serviceData.status}, ${this.serviceData.battery}), current:(${this.LeakSensor?.LeakDetected}, ${this.Battery.BatteryLevel})`) // LeakSensor - if (!this.device.waterdetector?.hide_leak && this.LeakSensor?.Service) { + if (!(this.device as waterDetectorConfig).hide_leak && this.LeakSensor?.Service) { // StatusActive this.LeakSensor.StatusActive = this.serviceData.state await this.debugLog(`StatusActive: ${this.LeakSensor.StatusActive}`) // LeakDetected - if (this.device.waterdetector?.dry) { + if ((this.device as waterDetectorConfig).dry) { this.LeakSensor.LeakDetected = this.serviceData.status === 0 ? 1 : 0 this.debugLog(`LeakDetected: ${this.LeakSensor.LeakDetected}`) } else { @@ -181,13 +178,13 @@ export class WaterDetector extends deviceBase { await this.debugLog(`(status, battery) = OpenAPI: (${this.deviceStatus.status}, ${this.deviceStatus.battery}), current:(${this.LeakSensor?.LeakDetected}, ${this.Battery.BatteryLevel})`) // LeakSensor - if (!this.device.waterdetector?.hide_leak && this.LeakSensor?.Service) { + if (!(this.device as waterDetectorConfig).hide_leak && this.LeakSensor?.Service) { // StatusActive this.LeakSensor.StatusActive = this.deviceStatus.battery !== 0 await this.debugLog(`StatusActive: ${this.LeakSensor.StatusActive}`) // LeakDetected - if (this.device.waterdetector?.dry) { + if ((this.device as waterDetectorConfig).dry) { this.LeakSensor.LeakDetected = this.deviceStatus.status === 0 ? 1 : 0 this.debugLog(`LeakDetected: ${this.LeakSensor.LeakDetected}`) } else { @@ -227,13 +224,13 @@ export class WaterDetector extends deviceBase { await this.debugLog(`(detectionState, battery) = Webhook: (${this.webhookContext.detectionState}, ${this.webhookContext.battery}), current:(${this.LeakSensor?.LeakDetected}, ${this.Battery.BatteryLevel})`) // LeakSensor - if (!this.device.waterdetector?.hide_leak && this.LeakSensor?.Service) { + if (!(this.device as waterDetectorConfig).hide_leak && this.LeakSensor?.Service) { // StatusActive this.LeakSensor.StatusActive = !!this.webhookContext.detectionState await this.debugLog(`StatusActive: ${this.LeakSensor.StatusActive}`) // LeakDetected - if (this.device.waterdetector?.dry) { + if ((this.device as waterDetectorConfig).dry) { this.LeakSensor.LeakDetected = this.webhookContext.detectionState === 0 ? 1 : 0 this.debugLog(`LeakDetected: ${this.LeakSensor.LeakDetected}`) } else { @@ -271,22 +268,22 @@ export class WaterDetector extends deviceBase { async BLERefreshStatus(): Promise { await this.debugLog('BLERefreshStatus') - const switchbot = await this.switchbotBLE() - if (switchbot === undefined) { - await this.BLERefreshConnection(switchbot) + const switchBotBLE = await this.switchbotBLE() + if (switchBotBLE === undefined) { + await this.BLERefreshConnection(switchBotBLE) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets - const serviceData = await this.monitorAdvertisementPackets(switchbot) as waterLeakDetectorServiceData + const serviceData = await this.monitorAdvertisementPackets(switchBotBLE) as waterLeakDetectorServiceData // Update HomeKit if (serviceData.model === SwitchBotBLEModel.Unknown && serviceData.modelName === SwitchBotBLEModelName.Unknown) { this.serviceData = serviceData await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.errorLog(`failed to get serviceData, serviceData: ${serviceData}`) - await this.BLERefreshConnection(switchbot) + await this.errorLog(`failed to get serviceData, serviceData: ${JSON.stringify(serviceData)}`) + await this.BLERefreshConnection(switchBotBLE) } })() } @@ -307,7 +304,7 @@ export class WaterDetector extends deviceBase { await this.BLEparseStatus() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle BLE. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } catch (error) { @@ -321,17 +318,17 @@ export class WaterDetector extends deviceBase { async openAPIRefreshStatus(): Promise { await this.debugLog('openAPIRefreshStatus') try { - const { body, statusCode } = await this.deviceRefreshStatus() - const deviceStatus: any = await body.json() - await this.debugLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.debugSuccessLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + const { body } = await this.deviceRefreshStatus() + const deviceStatus: any = await body + await this.debugLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.debugSuccessLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) this.deviceStatus = deviceStatus.body await this.openAPIparseStatus() await this.updateHomeKitCharacteristics() } else { - await this.debugWarnLog(`statusCode: ${statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) - await this.debugWarnLog(statusCode, deviceStatus) + await this.debugWarnLog(`statusCode: ${deviceStatus.statusCode}, deviceStatus: ${JSON.stringify(deviceStatus)}`) + await this.debugWarnLog(deviceStatus) } } catch (e: any) { await this.apiError(e) @@ -349,7 +346,7 @@ export class WaterDetector extends deviceBase { await this.parseStatusWebhook() await this.updateHomeKitCharacteristics() } catch (e: any) { - await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`) + await this.errorLog(`failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e.message ?? e}`) } } } else { @@ -361,7 +358,7 @@ export class WaterDetector extends deviceBase { * Updates the status for each of the HomeKit Characteristics */ async updateHomeKitCharacteristics(): Promise { - if (!this.device.waterdetector?.hide_leak && this.LeakSensor?.Service) { + if (!(this.device as waterDetectorConfig).hide_leak && this.LeakSensor?.Service) { // StatusActive await this.updateCharacteristic(this.LeakSensor.Service, this.hap.Characteristic.StatusActive, this.LeakSensor.StatusActive, 'StatusActive') // LeakDetected @@ -383,7 +380,7 @@ export class WaterDetector extends deviceBase { async offlineOff(): Promise { if (this.device.offline) { - if (!this.device.waterdetector?.hide_leak && this.LeakSensor?.Service) { + if (!(this.device as waterDetectorConfig).hide_leak && this.LeakSensor?.Service) { this.LeakSensor.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, false) this.LeakSensor.Service.updateCharacteristic(this.hap.Characteristic.LeakDetected, this.hap.Characteristic.LeakDetected.LEAK_NOT_DETECTED) } @@ -391,7 +388,7 @@ export class WaterDetector extends deviceBase { } async apiError(e: any): Promise { - if (!this.device.waterdetector?.hide_leak && this.LeakSensor?.Service) { + if (!(this.device as waterDetectorConfig).hide_leak && this.LeakSensor?.Service) { this.LeakSensor.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e) this.LeakSensor.Service.updateCharacteristic(this.hap.Characteristic.LeakDetected, e) } diff --git a/src/irdevice/airconditioner.ts b/src/irdevice/airconditioner.ts index bb379a51..4eab629a 100644 --- a/src/irdevice/airconditioner.ts +++ b/src/irdevice/airconditioner.ts @@ -3,10 +3,10 @@ * airconditioners.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, irdevice } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { irDevicesConfig } from '../settings.js' -import type { irdevice } from '../types/irdevicelist.js' +import type { irAirConfig, irDevicesConfig } from '../settings.js' import { irdeviceBase } from './irdevice.js' @@ -124,8 +124,8 @@ export class AirConditioner extends irdeviceBase { // Initialize HumiditySensor property - if (this.device.irair?.meterType && this.device.irair?.meterId) { - const meterUuid = this.platform.api.hap.uuid.generate(`${this.device.irair.meterId}-${this.device.irair.meterType}`) + if ((this.device as irAirConfig).meterType && (this.device as irAirConfig).meterId) { + const meterUuid = this.platform.api.hap.uuid.generate(`${(device as irAirConfig).meterId}-${(device as irAirConfig).meterType}`) this.meter = this.platform.accessories.find(accessory => accessory.UUID === meterUuid) accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {} this.HumiditySensor = { @@ -136,8 +136,8 @@ export class AirConditioner extends irdeviceBase { accessory.context.HumiditySensor = this.HumiditySensor as object } - if (this.device.irair?.meterType && this.device.irair?.meterId) { - const meterUuid = this.platform.api.hap.uuid.generate(`${this.device.irair.meterId}-${this.device.irair.meterType}`) + if ((this.device as irAirConfig).meterType && (this.device as irAirConfig).meterId) { + const meterUuid = this.platform.api.hap.uuid.generate(`${(device as irAirConfig).meterId}-${(device as irAirConfig).meterType}`) this.meter = this.platform.accessories.find(accessory => accessory.UUID === meterUuid) } @@ -162,11 +162,11 @@ export class AirConditioner extends irdeviceBase { if (this.HeaterCooler.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType() const command: string = await this.commandOn() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange) } } @@ -176,11 +176,11 @@ export class AirConditioner extends irdeviceBase { if (this.HeaterCooler.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOff) { const commandType: string = await this.commandType() const command: string = await this.commandOff() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange) } } @@ -219,12 +219,11 @@ export class AirConditioner extends irdeviceBase { const parameter = `${this.HeaterCooler.ThresholdTemperature},${this.CurrentMode},${this.CurrentFanSpeed},${this.state}` await this.UpdateCurrentHeaterCoolerState() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'setAll', parameter: `${parameter}`, commandType: 'command', - }) - + } await this.pushChanges(bodyChange) } @@ -254,38 +253,38 @@ export class AirConditioner extends irdeviceBase { this.HeaterCooler.RotationSpeed = this.HeaterCooler.RotationSpeed || this.accessory.context.RotationSpeed } - if (this.device.irair?.hide_automode) { - this.hide_automode = this.device.irair?.hide_automode + if ((this.device as irAirConfig).hide_automode) { + this.hide_automode = (this.device as irAirConfig).hide_automode this.accessory.context.hide_automode = this.hide_automode } else { - this.hide_automode = this.device.irair?.hide_automode + this.hide_automode = (this.device as irAirConfig).hide_automode this.accessory.context.hide_automode = this.hide_automode } - if (this.device.irair?.set_max_heat) { - this.set_max_heat = this.device.irair?.set_max_heat + if ((this.device as irAirConfig).set_max_heat) { + this.set_max_heat = (this.device as irAirConfig).set_max_heat this.accessory.context.set_max_heat = this.set_max_heat } else { this.set_max_heat = 35 this.accessory.context.set_max_heat = this.set_max_heat } - if (this.device.irair?.set_min_heat) { - this.set_min_heat = this.device.irair?.set_min_heat + if ((this.device as irAirConfig).set_min_heat) { + this.set_min_heat = (this.device as irAirConfig).set_min_heat this.accessory.context.set_min_heat = this.set_min_heat } else { this.set_min_heat = 0 this.accessory.context.set_min_heat = this.set_min_heat } - if (this.device.irair?.set_max_cool) { - this.set_max_cool = this.device.irair?.set_max_cool + if ((this.device as irAirConfig).set_max_cool) { + this.set_max_cool = (this.device as irAirConfig).set_max_cool this.accessory.context.set_max_cool = this.set_max_cool } else { this.set_max_cool = 35 this.accessory.context.set_max_cool = this.set_max_cool } - if (this.device.irair?.set_min_cool) { - this.set_min_cool = this.device.irair?.set_min_cool + if ((this.device as irAirConfig).set_min_cool) { + this.set_min_cool = (this.device as irAirConfig).set_min_cool this.accessory.context.set_min_cool = this.set_min_cool } else { this.set_min_cool = 0 @@ -317,14 +316,13 @@ export class AirConditioner extends irdeviceBase { if (this.device.connectionType === 'OpenAPI' && !this.disablePushDetail) { await this.infoLog(`Sending request to SwitchBot API, body: ${bodyChange},`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.pushStatusCodes(statusCode, deviceStatus) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.successfulPushChange(statusCode, deviceStatus, bodyChange) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.pushStatusCodes(deviceStatus) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.successfulPushChange(deviceStatus, bodyChange) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -527,10 +525,10 @@ export class AirConditioner extends irdeviceBase { } async getAirConditionerConfigSettings(accessory: PlatformAccessory, device: irdevice & irDevicesConfig): Promise { - accessory.context.hide_automode = this.hide_automode = device.irair?.hide_automode - accessory.context.set_max_heat = this.set_max_heat = device.irair?.set_max_heat ?? 35 - accessory.context.set_min_heat = this.set_min_heat = device.irair?.set_min_heat ?? 0 - accessory.context.set_max_cool = this.set_max_cool = device.irair?.set_max_cool ?? 35 - accessory.context.set_min_cool = this.set_min_cool = device.irair?.set_min_cool ?? 0 + accessory.context.hide_automode = this.hide_automode = (device as irAirConfig).hide_automode + accessory.context.set_max_heat = this.set_max_heat = (device as irAirConfig).set_max_heat ?? 35 + accessory.context.set_min_heat = this.set_min_heat = (device as irAirConfig).set_min_heat ?? 0 + accessory.context.set_max_cool = this.set_max_cool = (device as irAirConfig).set_max_cool ?? 35 + accessory.context.set_min_cool = this.set_min_cool = (device as irAirConfig).set_min_cool ?? 0 } } diff --git a/src/irdevice/airpurifier.ts b/src/irdevice/airpurifier.ts index 9c7187d8..a3f98d73 100644 --- a/src/irdevice/airpurifier.ts +++ b/src/irdevice/airpurifier.ts @@ -3,10 +3,10 @@ * airpurifier.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, irdevice } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' import type { irDevicesConfig } from '../settings.js' -import type { irdevice } from '../types/irdevicelist.js' import { irdeviceBase } from './irdevice.js' @@ -149,11 +149,11 @@ export class AirPurifier extends irdeviceBase { if (this.AirPurifier.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType() const command: string = await this.commandOn() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange) } } @@ -163,11 +163,11 @@ export class AirPurifier extends irdeviceBase { if (this.AirPurifier.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType() const command: string = await this.commandOff() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange) } } @@ -191,11 +191,11 @@ export class AirPurifier extends irdeviceBase { this.CurrentAPFanSpeed = this.CurrentFanSpeed ?? 1 this.APActive = this.AirPurifier.Active === 1 ? 'on' : 'off' const parameter = `${this.CurrentAPTemp},${this.CurrentAPMode},${this.CurrentAPFanSpeed},${this.APActive}` - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'setAll', parameter: `${parameter}`, commandType: 'command', - }) + } if (this.AirPurifier.Active === 1) { if ((Number(this.TemperatureSensor!.CurrentTemperature) || 24) < (this.LastTemperature || 30)) { this.AirPurifier.CurrentHeaterCoolerState = this.hap.Characteristic.CurrentHeaterCoolerState.COOLING @@ -213,14 +213,13 @@ export class AirPurifier extends irdeviceBase { if (this.device.connectionType === 'OpenAPI') { this.infoLog(`Sending request to SwitchBot API, body: ${bodyChange},`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.pushStatusCodes(statusCode, deviceStatus) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.successfulPushChange(statusCode, deviceStatus, bodyChange) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.pushStatusCodes(deviceStatus) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.successfulPushChange(deviceStatus, bodyChange) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { diff --git a/src/irdevice/camera.ts b/src/irdevice/camera.ts index 69dfa1e1..e42eee90 100644 --- a/src/irdevice/camera.ts +++ b/src/irdevice/camera.ts @@ -3,10 +3,10 @@ * camera.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, irdevice } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' import type { irDevicesConfig } from '../settings.js' -import type { irdevice } from '../types/irdevicelist.js' import { irdeviceBase } from './irdevice.js' @@ -72,11 +72,11 @@ export class Camera extends irdeviceBase { if (this.Switch.On && !this.disablePushOn) { const commandType: string = await this.commandType() const command: string = await this.commandOn() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange) } } @@ -86,11 +86,11 @@ export class Camera extends irdeviceBase { if (!this.Switch.On && !this.disablePushOff) { const commandType: string = await this.commandType() const command: string = await this.commandOff() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange) } } @@ -100,14 +100,13 @@ export class Camera extends irdeviceBase { if (this.device.connectionType === 'OpenAPI') { this.infoLog(`Sending request to SwitchBot API, body: ${bodyChange},`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.pushStatusCodes(statusCode, deviceStatus) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.successfulPushChange(statusCode, deviceStatus, bodyChange) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.pushStatusCodes(deviceStatus) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.successfulPushChange(deviceStatus, bodyChange) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { diff --git a/src/irdevice/fan.ts b/src/irdevice/fan.ts index f6e8d2af..929fda97 100644 --- a/src/irdevice/fan.ts +++ b/src/irdevice/fan.ts @@ -3,10 +3,10 @@ * fan.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, irdevice } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { irDevicesConfig } from '../settings.js' -import type { irdevice } from '../types/irdevicelist.js' +import type { irDevicesConfig, irFanConfig } from '../settings.js' import { irdeviceBase } from './irdevice.js' @@ -51,16 +51,16 @@ export class IRFan extends irdeviceBase { return this.Fan.Active }).onSet(this.ActiveSet.bind(this)) - if (device.irfan?.rotation_speed) { + if ((device as irFanConfig).rotation_speed) { // handle Rotation Speed events using the RotationSpeed characteristic this.Fan.Service.getCharacteristic(this.hap.Characteristic.RotationSpeed).setProps({ - minStep: device.irfan?.set_minStep ?? 1, - minValue: device.irfan?.set_min ?? 1, - maxValue: device.irfan?.set_max ?? 100, + minStep: (device as irFanConfig).set_minStep ?? 1, + minValue: (device as irFanConfig).set_min ?? 1, + maxValue: (device as irFanConfig).set_max ?? 100, }).onGet(() => { return this.Fan.RotationSpeed }).onSet(this.RotationSpeedSet.bind(this)) - } else if (this.Fan.Service.testCharacteristic(this.hap.Characteristic.RotationSpeed) && !device.irfan?.swing_mode) { + } else if (this.Fan.Service.testCharacteristic(this.hap.Characteristic.RotationSpeed) && !(device as irFanConfig).swing_mode) { const characteristic = this.Fan.Service.getCharacteristic(this.hap.Characteristic.RotationSpeed) this.Fan.Service.removeCharacteristic(characteristic) this.debugLog('Rotation Speed Characteristic was removed.') @@ -68,12 +68,12 @@ export class IRFan extends irdeviceBase { this.debugLog(`RotationSpeed Characteristic was not removed/added, Clear Cache on ${this.accessory.displayName} to remove Chracteristic`) } - if (device.irfan?.swing_mode) { + if ((device as irFanConfig).swing_mode) { // handle Osolcation events using the SwingMode characteristic this.Fan.Service.getCharacteristic(this.hap.Characteristic.SwingMode).onGet(() => { return this.Fan.SwingMode }).onSet(this.SwingModeSet.bind(this)) - } else if (this.Fan.Service.testCharacteristic(this.hap.Characteristic.SwingMode) && !device.irfan?.swing_mode) { + } else if (this.Fan.Service.testCharacteristic(this.hap.Characteristic.SwingMode) && !(device as irFanConfig).swing_mode) { const characteristic = this.Fan.Service.getCharacteristic(this.hap.Characteristic.SwingMode) this.Fan.Service.removeCharacteristic(characteristic) this.debugLog('Swing Mode Characteristic was removed.') @@ -136,11 +136,11 @@ export class IRFan extends irdeviceBase { if (this.Fan.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType() const command: string = await this.commandOn() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange) } } @@ -150,39 +150,39 @@ export class IRFan extends irdeviceBase { if (this.Fan.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOff) { const commandType: string = await this.commandType() const command: string = await this.commandOff() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange) } } async pushFanSpeedUpChanges(): Promise { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'highSpeed', parameter: 'default', commandType: 'command', - }) + } await this.pushChanges(bodyChange) } async pushFanSpeedDownChanges(): Promise { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'lowSpeed', parameter: 'default', commandType: 'command', - }) + } await this.pushChanges(bodyChange) } async pushFanSwingChanges(): Promise { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'swing', parameter: 'default', commandType: 'command', - }) + } await this.pushChanges(bodyChange) } @@ -191,14 +191,13 @@ export class IRFan extends irdeviceBase { if (this.device.connectionType === 'OpenAPI') { this.infoLog(`Sending request to SwitchBot API, body: ${bodyChange},`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.pushStatusCodes(statusCode, deviceStatus) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.successfulPushChange(statusCode, deviceStatus, bodyChange) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.pushStatusCodes(deviceStatus) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.successfulPushChange(deviceStatus, bodyChange) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { diff --git a/src/irdevice/irdevice.ts b/src/irdevice/irdevice.ts index 61273af0..4c4f2768 100644 --- a/src/irdevice/irdevice.ts +++ b/src/irdevice/irdevice.ts @@ -3,14 +3,10 @@ * device.ts: @switchbot/homebridge-switchbot. */ import type { API, CharacteristicValue, HAP, Logging, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, irdevice } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { irDevicesConfig, SwitchBotPlatformConfig } from '../settings.js' -import type { irdevice } from '../types/irdevicelist.js' - -import { request } from 'undici' - -import { Devices } from '../settings.js' +import type { irAirConfig, irDevicesConfig, irFanConfig, irLightConfig, irOtherConfig, SwitchBotPlatformConfig } from '../settings.js' export abstract class irdeviceBase { public readonly api: API @@ -82,18 +78,30 @@ export abstract class irdeviceBase { device.disablePushOff === true && { disablePushOff: device.disablePushOff }, device.disablePushDetail === true && { disablePushDetail: device.disablePushDetail }, ) + let deviceSpecificConfig = {} + switch (device.configRemoteType) { + case 'Fan': + case 'DIY Fan': + deviceSpecificConfig = device as irFanConfig + break + case 'Light': + case 'DIY Light': + deviceSpecificConfig = device as irLightConfig + break + case 'Air Conditioner': + case 'DIY Air Conditioner': + deviceSpecificConfig = device as irAirConfig + break + case 'Others': + deviceSpecificConfig = device as irOtherConfig + break + default: + break + } const config = Object.assign( {}, deviceConfig, - device.irair, - device.irpur, - device.ircam, - device.irfan, - device.irlight, - device.other, - device.irtv, - device.irvc, - device.irwh, + deviceSpecificConfig, ) if (Object.keys(config).length !== 0) { this.debugSuccessLog(`Config: ${JSON.stringify(config)}`) @@ -129,16 +137,13 @@ export abstract class irdeviceBase { this.debugSuccessLog(`version: ${accessory.context.version}`) } - async pushChangeRequest(bodyChange: string): Promise<{ body: any, statusCode: any }> { - return await request(`${Devices}/${this.device.deviceId}/commands`, { - body: bodyChange, - method: 'POST', - headers: this.platform.generateHeaders(), - }) + async pushChangeRequest(bodyChange: bodyChange): Promise<{ body: any, statusCode: number }> { + const { response, statusCode } = await this.platform.switchBotAPI.controlDevice(this.device.deviceId, bodyChange.command, bodyChange.parameter, bodyChange.commandType) + return { body: response, statusCode } } - async successfulStatusCodes(statusCode: any, deviceStatus: any) { - return (statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100) + async successfulStatusCodes(deviceStatus: any) { + return (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100) } /** @@ -163,14 +168,13 @@ export abstract class irdeviceBase { } } - async pushStatusCodes(statusCode: any, deviceStatus: any) { - await this.debugWarnLog(`statusCode: ${statusCode}`) + async pushStatusCodes(deviceStatus: any) { await this.debugWarnLog(`deviceStatus: ${JSON.stringify(deviceStatus)}`) await this.debugWarnLog(`deviceStatus statusCode: ${deviceStatus.statusCode}`) } - async successfulPushChange(statusCode: any, deviceStatus: any, bodyChange: any) { - this.debugSuccessLog(`statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`) + async successfulPushChange(deviceStatus: any, bodyChange: any) { + this.debugSuccessLog(`deviceStatus StatusCode: ${deviceStatus.statusCode}`) this.successLog(`request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`) } diff --git a/src/irdevice/light.ts b/src/irdevice/light.ts index bf7ae89b..29b7dc01 100644 --- a/src/irdevice/light.ts +++ b/src/irdevice/light.ts @@ -3,10 +3,10 @@ * light.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, irdevice } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { irDevicesConfig } from '../settings.js' -import type { irdevice } from '../types/irdevicelist.js' +import type { irDevicesConfig, irLightConfig } from '../settings.js' import { irdeviceBase } from './irdevice.js' @@ -46,7 +46,7 @@ export class Light extends irdeviceBase { // Set category accessory.category = this.hap.Categories.LIGHTBULB - if (!device.irlight?.stateless) { + if (!(device as irLightConfig).stateless) { // Initialize LightBulb Service accessory.context.LightBulb = accessory.context.LightBulb ?? {} this.LightBulb = { @@ -170,11 +170,11 @@ export class Light extends irdeviceBase { if (On === true && this.disablePushOn === false) { const commandType: string = await this.commandType() const command: string = await this.commandOn() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange, On) } } @@ -184,11 +184,11 @@ export class Light extends irdeviceBase { if (On === false && this.disablePushOff === false) { const commandType: string = await this.commandType() const command: string = await this.commandOff() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange, On) } } @@ -198,15 +198,14 @@ export class Light extends irdeviceBase { if (this.device.connectionType === 'OpenAPI') { this.infoLog(`Sending request to SwitchBot API, body: ${bodyChange},`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.pushStatusCodes(statusCode, deviceStatus) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.successfulPushChange(statusCode, deviceStatus, bodyChange) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.pushStatusCodes(deviceStatus) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.successfulPushChange(deviceStatus, bodyChange) this.accessory.context.On = On await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -220,7 +219,7 @@ export class Light extends irdeviceBase { async updateHomeKitCharacteristics(): Promise { await this.debugLog('updateHomeKitCharacteristics') - if (!this.device.irlight?.stateless && this.LightBulb?.Service) { + if (!(this.device as irLightConfig).stateless && this.LightBulb?.Service) { // On await this.updateCharacteristic(this.LightBulb.Service, this.hap.Characteristic.On, this.LightBulb.On, 'On') } else { @@ -236,7 +235,7 @@ export class Light extends irdeviceBase { } async apiError(e: any): Promise { - if (!this.device.irlight?.stateless) { + if (!(this.device as irLightConfig).stateless) { this.LightBulb?.Service.updateCharacteristic(this.hap.Characteristic.On, e) } else { this.ProgrammableSwitchOn?.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e) diff --git a/src/irdevice/other.ts b/src/irdevice/other.ts index aef8eacf..20898eb1 100644 --- a/src/irdevice/other.ts +++ b/src/irdevice/other.ts @@ -3,10 +3,10 @@ * other.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, irdevice } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' -import type { irDevicesConfig } from '../settings.js' -import type { irdevice } from '../types/irdevicelist.js' +import type { irDevicesConfig, irOtherConfig } from '../settings.js' import { irdeviceBase } from './irdevice.js' @@ -412,11 +412,11 @@ export class Others extends irdeviceBase { if (On === true && !this.disablePushOn) { const commandType: string = await this.commandType() const command: string = await this.commandOn() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange) } } else { @@ -430,11 +430,11 @@ export class Others extends irdeviceBase { if (On === false && !this.disablePushOff) { const commandType: string = await this.commandType() const command: string = await this.commandOff() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange) } } else { @@ -447,14 +447,13 @@ export class Others extends irdeviceBase { if (this.device.connectionType === 'OpenAPI') { this.infoLog(`Sending request to SwitchBot API, body: ${bodyChange},`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.pushStatusCodes(statusCode, deviceStatus) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.successfulPushChange(statusCode, deviceStatus, bodyChange) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.pushStatusCodes(deviceStatus) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.successfulPushChange(deviceStatus, bodyChange) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { @@ -785,10 +784,8 @@ export class Others extends irdeviceBase { } async getOtherConfigSettings(accessory: PlatformAccessory, device: irdevice & irDevicesConfig): Promise { - this.otherDeviceType = accessory.context.otherDeviceType - ? accessory.context.otherDeviceType - : device.other?.deviceType ? device.other.deviceType : 'outlet' - const deviceType = accessory.context.otherDeviceType ? 'Accessory Cache' : device.other?.deviceType ? 'Device Config' : 'Default' - await this.debugLog(`Use ${deviceType} Type: ${this.otherDeviceType}`) + this.otherDeviceType = (device as irOtherConfig).type ?? 'outlet' + const deviceType = (device as irOtherConfig).type ? 'Device Config' : 'Default' + await this.debugLog(`Use ${deviceType} Device Type: ${this.otherDeviceType}`) } } diff --git a/src/irdevice/tv.ts b/src/irdevice/tv.ts index f3177852..ce4229da 100644 --- a/src/irdevice/tv.ts +++ b/src/irdevice/tv.ts @@ -3,10 +3,10 @@ * tv.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, irdevice } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' import type { irDevicesConfig } from '../settings.js' -import type { irdevice } from '../types/irdevicelist.js' import { irdeviceBase } from './irdevice.js' @@ -210,11 +210,11 @@ export class TV extends irdeviceBase { if (this.Television.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType() const command: string = await this.commandOn() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushTVChanges(bodyChange) } } @@ -224,11 +224,11 @@ export class TV extends irdeviceBase { if (this.Television.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOff) { const commandType: string = await this.commandType() const command: string = await this.commandOff() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushTVChanges(bodyChange) } } @@ -236,11 +236,11 @@ export class TV extends irdeviceBase { async pushOkChanges(): Promise { await this.debugLog(`pushOkChanges disablePushDetail: ${this.disablePushDetail}`) if (!this.disablePushDetail) { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'Ok', parameter: 'default', commandType: 'command', - }) + } await this.pushTVChanges(bodyChange) } } @@ -248,11 +248,11 @@ export class TV extends irdeviceBase { async pushBackChanges(): Promise { await this.debugLog(`pushBackChanges disablePushDetail: ${this.disablePushDetail}`) if (!this.disablePushDetail) { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'Back', parameter: 'default', commandType: 'command', - }) + } await this.pushTVChanges(bodyChange) } } @@ -260,11 +260,11 @@ export class TV extends irdeviceBase { async pushMenuChanges(): Promise { await this.debugLog(`pushMenuChanges disablePushDetail: ${this.disablePushDetail}`) if (!this.disablePushDetail) { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'Menu', parameter: 'default', commandType: 'command', - }) + } await this.pushTVChanges(bodyChange) } } @@ -272,11 +272,11 @@ export class TV extends irdeviceBase { async pushUpChanges(): Promise { await this.debugLog(`pushUpChanges disablePushDetail: ${this.disablePushDetail}`) if (!this.disablePushDetail) { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'Up', parameter: 'default', commandType: 'command', - }) + } await this.pushTVChanges(bodyChange) } } @@ -284,11 +284,11 @@ export class TV extends irdeviceBase { async pushDownChanges(): Promise { await this.debugLog(`pushDownChanges disablePushDetail: ${this.disablePushDetail}`) if (!this.disablePushDetail) { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'Down', parameter: 'default', commandType: 'command', - }) + } await this.pushTVChanges(bodyChange) } } @@ -296,11 +296,11 @@ export class TV extends irdeviceBase { async pushRightChanges(): Promise { await this.debugLog(`pushRightChanges disablePushDetail: ${this.disablePushDetail}`) if (!this.disablePushDetail) { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'Right', parameter: 'default', commandType: 'command', - }) + } await this.pushTVChanges(bodyChange) } } @@ -308,11 +308,11 @@ export class TV extends irdeviceBase { async pushLeftChanges(): Promise { await this.debugLog(`pushLeftChanges disablePushDetail: ${this.disablePushDetail}`) if (!this.disablePushDetail) { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'Left', parameter: 'default', commandType: 'command', - }) + } await this.pushTVChanges(bodyChange) } } @@ -320,11 +320,11 @@ export class TV extends irdeviceBase { async pushVolumeUpChanges(): Promise { await this.debugLog(`pushVolumeUpChanges disablePushDetail: ${this.disablePushDetail}`) if (!this.disablePushDetail) { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'volumeAdd', parameter: 'default', commandType: 'command', - }) + } await this.pushTVChanges(bodyChange) } } @@ -332,11 +332,11 @@ export class TV extends irdeviceBase { async pushVolumeDownChanges(): Promise { await this.debugLog(`pushVolumeDownChanges disablePushDetail: ${this.disablePushDetail}`) if (!this.disablePushDetail) { - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command: 'volumeSub', parameter: 'default', commandType: 'command', - }) + } await this.pushTVChanges(bodyChange) } } @@ -346,14 +346,13 @@ export class TV extends irdeviceBase { if (this.device.connectionType === 'OpenAPI') { await this.infoLog(`Sending request to SwitchBot API, body: ${bodyChange},`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.pushStatusCodes(statusCode, deviceStatus) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.successfulPushChange(statusCode, deviceStatus, bodyChange) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.pushStatusCodes(deviceStatus) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.successfulPushChange(deviceStatus, bodyChange) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { diff --git a/src/irdevice/vacuumcleaner.ts b/src/irdevice/vacuumcleaner.ts index f8cacb62..4d5554c9 100644 --- a/src/irdevice/vacuumcleaner.ts +++ b/src/irdevice/vacuumcleaner.ts @@ -3,10 +3,10 @@ * vacuumcleaner.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, irdevice } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' import type { irDevicesConfig } from '../settings.js' -import type { irdevice } from '../types/irdevicelist.js' import { irdeviceBase } from './irdevice.js' @@ -69,11 +69,11 @@ export class VacuumCleaner extends irdeviceBase { if (this.Switch.On && !this.disablePushOn) { const commandType: string = await this.commandType() const command: string = await this.commandOn() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange) } } @@ -83,11 +83,11 @@ export class VacuumCleaner extends irdeviceBase { if (!this.Switch.On && !this.disablePushOff) { const commandType: string = await this.commandType() const command: string = await this.commandOff() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange) } } @@ -97,14 +97,13 @@ export class VacuumCleaner extends irdeviceBase { if (this.device.connectionType === 'OpenAPI') { await this.infoLog(`Sending request to SwitchBot API, body: ${bodyChange},`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.pushStatusCodes(statusCode, deviceStatus) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.successfulPushChange(statusCode, deviceStatus, bodyChange) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.pushStatusCodes(deviceStatus) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.successfulPushChange(deviceStatus, bodyChange) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { diff --git a/src/irdevice/waterheater.ts b/src/irdevice/waterheater.ts index 4a4132f4..fc1031b8 100644 --- a/src/irdevice/waterheater.ts +++ b/src/irdevice/waterheater.ts @@ -3,10 +3,10 @@ * waterheater.ts: @switchbot/homebridge-switchbot. */ import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge' +import type { bodyChange, irdevice } from 'node-switchbot' import type { SwitchBotPlatform } from '../platform.js' import type { irDevicesConfig } from '../settings.js' -import type { irdevice } from '../types/irdevicelist.js' import { irdeviceBase } from './irdevice.js' @@ -70,11 +70,11 @@ export class WaterHeater extends irdeviceBase { if (this.Valve.Active === this.hap.Characteristic.Active.ACTIVE && !this.disablePushOn) { const commandType: string = await this.commandType() const command: string = await this.commandOn() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange) } } @@ -84,11 +84,11 @@ export class WaterHeater extends irdeviceBase { if (this.Valve.Active === this.hap.Characteristic.Active.INACTIVE && !this.disablePushOff) { const commandType: string = await this.commandType() const command: string = await this.commandOff() - const bodyChange = JSON.stringify({ + const bodyChange: bodyChange = { command, parameter: 'default', commandType, - }) + } await this.pushChanges(bodyChange) } } @@ -98,14 +98,13 @@ export class WaterHeater extends irdeviceBase { if (this.device.connectionType === 'OpenAPI') { this.infoLog(`Sending request to SwitchBot API, body: ${bodyChange},`) try { - const { body, statusCode } = await this.pushChangeRequest(bodyChange) - const deviceStatus: any = await body.json() - await this.pushStatusCodes(statusCode, deviceStatus) - if (await this.successfulStatusCodes(statusCode, deviceStatus)) { - await this.successfulPushChange(statusCode, deviceStatus, bodyChange) + const { body } = await this.pushChangeRequest(bodyChange) + const deviceStatus: any = await body + await this.pushStatusCodes(deviceStatus) + if (await this.successfulStatusCodes(deviceStatus)) { + await this.successfulPushChange(deviceStatus, bodyChange) await this.updateHomeKitCharacteristics() } else { - await this.statusCode(statusCode) await this.statusCode(deviceStatus.statusCode) } } catch (e: any) { diff --git a/src/platform.ts b/src/platform.ts index 9ee54fc2..af75e68d 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -2,21 +2,14 @@ * * platform.ts: @switchbot/homebridge-switchbot platform class. */ -import type { IncomingMessage, Server, ServerResponse } from 'node:http' -import type { UrlObject } from 'node:url' +import type { Server } from 'node:http' import type { API, DynamicPlatformPlugin, Logging, PlatformAccessory } from 'homebridge' import type { MqttClient } from 'mqtt' -import type { Dispatcher } from 'undici' -import type { devicesConfig, irDevicesConfig, options, SwitchBotPlatformConfig } from './settings.js' -import type { blindTilt, curtain, curtain3, device } from './types/devicelist.js' -import type { irdevice } from './types/irdevicelist.js' +import type { blindTiltConfig, curtainConfig, devicesConfig, irDevicesConfig, options, SwitchBotPlatformConfig } from './settings.js' -import { Buffer } from 'node:buffer' -import crypto, { randomUUID } from 'node:crypto' -import { readFileSync, writeFileSync } from 'node:fs' -import { createServer } from 'node:http' +import { readFileSync } from 'node:fs' import process from 'node:process' import asyncmqtt from 'async-mqtt' @@ -24,11 +17,13 @@ import fakegato from 'fakegato-history' import { EveHomeKitTypes } from 'homebridge-lib/EveHomeKitTypes' /* * For Testing Locally: -* import { SwitchBotModel } from '/Users/Shared/GitHub/OpenWonderLabs/node-switchbot/dist/index.js'; +* import type { blindTilt, curtain, curtain3, device, irdevice } from '/Users/Shared/GitHub/OpenWonderLabs/node-switchbot/dist/index.js'; +* import { LogLevel, SwitchBotBLE, SwitchBotModel, SwitchBotOpenAPI } from '/Users/Shared/GitHub/OpenWonderLabs/node-switchbot/dist/index.js'; */ -import { SwitchBot, SwitchBotModel } from 'node-switchbot' +import type { blindTilt, curtain, curtain3, device, deviceStatus, deviceStatusRequest, irdevice } from 'node-switchbot' + +import { LogLevel, SwitchBotBLE, SwitchBotModel, SwitchBotOpenAPI } from 'node-switchbot' import { queueScheduler } from 'rxjs' -import { request } from 'undici' import { BlindTilt } from './device/blindtilt.js' import { Bot } from './device/bot.js' @@ -57,7 +52,7 @@ import { Others } from './irdevice/other.js' import { TV } from './irdevice/tv.js' import { VacuumCleaner } from './irdevice/vacuumcleaner.js' import { WaterHeater } from './irdevice/waterheater.js' -import { deleteWebhook, Devices, PLATFORM_NAME, PLUGIN_NAME, queryWebhook, setupWebhook, updateWebhook } from './settings.js' +import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js' import { formatDeviceIdAsMac, isBlindTiltDevice, isCurtainDevice, sleep } from './utils.js' /** @@ -85,6 +80,10 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { mqttClient: MqttClient | null = null webhookEventListener: Server | null = null + // SwitchBot APIs + switchBotAPI!: SwitchBotOpenAPI + switchBotBLE!: SwitchBotBLE + // External APIs public readonly eve: any public readonly fakegatoAPI: any @@ -103,6 +102,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { // only load if configured if (!config) { + this.log.error('No configuration found for the plugin, please check your config.') return } @@ -112,6 +112,8 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { name: config.name, credentials: config.credentials as object, options: config.options as object, + devices: config.devices as { deviceId: string }[], + deviceConfig: config.deviceConfig as { [deviceType: string]: devicesConfig }, } // Plugin Configuration @@ -127,11 +129,51 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.verifyConfig() this.debugLog('Config OK') } catch (e: any) { - this.errorLog(`Verify Config, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug') - this.debugErrorLog(`Verify Config, Error: ${e}`) + this.errorLog(`Verify Config, Error Message: ${e.message ?? e}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug') + this.debugErrorLog(`Verify Config, Error: ${e.message ?? e}`) return } + // SwitchBot OpenAPI + if (this.config.credentials?.token && this.config.credentials?.secret) { + this.switchBotAPI = new SwitchBotOpenAPI(this.config.credentials.token, this.config.credentials.secret) + } else { + this.debugErrorLog('Missing SwitchBot API credentials (token or secret).') + } + // Listen for log events + if (!this.config.options?.disableLogsforOpenAPI && this.switchBotAPI) { + this.switchBotAPI.on('log', (log) => { + switch (log.level) { + case LogLevel.SUCCESS: + this.successLog(log.message) + break + case LogLevel.DEBUGSUCCESS: + this.debugSuccessLog(log.message) + break + case LogLevel.WARN: + this.warnLog(log.message) + break + case LogLevel.DEBUGWARN: + this.debugWarnLog(log.message) + break + case LogLevel.ERROR: + this.errorLog(log.message) + break + case LogLevel.DEBUGERROR: + this.debugErrorLog(log.message) + break + case LogLevel.DEBUG: + this.debugLog(log.message) + break + case LogLevel.INFO: + default: + this.infoLog(log.message) + } + }) + } else { + this.debugErrorLog(`SwitchBot OpenAPI logs are disabled, enable it by setting disableLogsforOpenAPI to false.`) + this.debugLog(`SwitchBot OpenAPI: ${JSON.stringify(this.switchBotAPI)}, disableLogsforOpenAPI: ${this.config.options?.disableLogsforOpenAPI}`) + } // import fakegato-history module and EVE characteristics this.fakegatoAPI = fakegato(api) this.eve = new EveHomeKitTypes(api) @@ -144,33 +186,27 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { await this.debugLog('Executed didFinishLaunching callback') // run the method to discover / register your devices as accessories try { - if (this.config.credentials?.openToken && !this.config.credentials.token) { - await this.updateToken() - } else if (this.config.credentials?.token && !this.config.credentials?.secret) { - await this.errorLog('"secret" config is not populated, you must populate then please restart Homebridge.') - } else { - await this.discoverDevices() - } + await this.discoverDevices() } catch (e: any) { - await this.errorLog(`Failed to Discover, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug') - await this.debugErrorLog(`Failed to Discover, Error: ${e}`) + await this.errorLog(`Failed to Discover, Error Message: ${e.message ?? e}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug') + await this.debugErrorLog(`Failed to Discover, Error: ${e.message ?? e}`) } }) try { this.setupMqtt() } catch (e: any) { - this.errorLog(`Setup MQTT, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug') + this.errorLog(`Setup MQTT, Error Message: ${e.message ?? e}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug') } try { this.setupwebhook() } catch (e: any) { - this.errorLog(`Setup Webhook, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug') + this.errorLog(`Setup Webhook, Error Message: ${e.message ?? e}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug') } try { this.setupBlE() } catch (e: any) { - this.errorLog(`Setup Platform BLE, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug') + this.errorLog(`Setup Platform BLE, Error Message: ${e.message ?? e}, Submit Bugs Here: ` + 'https://tinyurl.com/SwitchBotBug') } } @@ -181,7 +217,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.mqttClient = await connectAsync(this.config.options?.mqttURL, this.config.options.mqttOptions || {}) await this.debugLog('MQTT connection has been established successfully.') this.mqttClient.on('error', async (e: Error) => { - await this.errorLog(`Failed to publish MQTT messages. ${e}`) + await this.errorLog(`Failed to publish MQTT messages. ${e.message ?? e}`) }) if (!this.config.options?.webhookURL) { // receive webhook events via MQTT @@ -193,13 +229,13 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { const context = JSON.parse(message.toString()) this.webhookEventHandler[context.deviceMac]?.(context) } catch (e: any) { - await this.errorLog(`Failed to handle webhook event. Error:${e}`) + await this.errorLog(`Failed to handle webhook event. Error:${e.message ?? e}`) } }) } - } catch (e) { + } catch (e: any) { this.mqttClient = null - await this.errorLog(`Failed to establish MQTT connection. ${e}`) + await this.errorLog(`Failed to establish MQTT connection. ${e.message ?? e}`) } } } @@ -208,156 +244,94 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { // webhook configuration if (this.config.options?.webhookURL) { const url = this.config.options?.webhookURL - try { - const xurl = new URL(url) - const port = Number(xurl.port) - const path = xurl.pathname - this.webhookEventListener = createServer((request: IncomingMessage, response: ServerResponse) => { - try { - if (request.url === path && request.method === 'POST') { - request.on('data', async (data) => { - try { - const body = JSON.parse(data) - await this.debugLog(`Received Webhook: ${JSON.stringify(body)}`) - if (this.config.options?.mqttURL) { - const mac = body.context.deviceMac?.toLowerCase().match(/[\s\S]{1,2}/g)?.join(':') - const options = this.config.options?.mqttPubOptions || {} - this.mqttClient?.publish(`homebridge-switchbot/webhook/${mac}`, `${JSON.stringify(body.context)}`, options) - } - this.webhookEventHandler[body.context.deviceMac]?.(body.context) - } catch (e: any) { - await this.errorLog(`Failed to handle webhook event. Error:${e}`) - } - }) - response.writeHead(200, { 'Content-Type': 'text/plain' }) - response.end('OK') - } - // else { - // response.writeHead(403, {'Content-Type': 'text/plain'}); - // response.end(`NG`); - // } - } catch (e: any) { - this.errorLog(`Failed to handle webhook event. Error:${e}`) + this.switchBotAPI.setupWebhook(url) + // Listen for webhook events + this.switchBotAPI.on('webhookEvent', (body) => { + if (this.config.options?.mqttURL) { + const mac = body.context.deviceMac?.toLowerCase().match(/[\s\S]{1,2}/g)?.join(':') + const options = this.config.options?.mqttPubOptions || {} + this.mqttClient?.publish(`homebridge-switchbot/webhook/${mac}`, `${JSON.stringify(body.context)}`, options) } - }).listen(port || 80) - } catch (e: any) { - await this.errorLog(`Failed to create webhook listener. Error:${e.message}`) - return - } - - try { - const { body, statusCode } = await request(setupWebhook, { - method: 'POST', - headers: this.generateHeaders(), - body: JSON.stringify({ - action: 'setupWebhook', - url, - deviceList: 'ALL', - }), + this.webhookEventHandler[body.context.deviceMac]?.(body.context) }) - const response: any = await body.json() - await this.debugLog(`setupWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`) - if (statusCode !== 200 || response?.statusCode !== 100) { - await this.errorLog(`Failed to configure webhook. Existing webhook well be overridden. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`) - } } catch (e: any) { - await this.errorLog(`Failed to configure webhook. Error: ${e.message}`) - } - - try { - const { body, statusCode } = await request(updateWebhook, { - method: 'POST', - headers: this.generateHeaders(), - body: JSON.stringify({ - action: 'updateWebhook', - config: { - url, - enable: true, - }, - }), - }) - const response: any = await body.json() - await this.debugLog(`updateWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`) - if (statusCode !== 200 || response?.statusCode !== 100) { - await this.errorLog(`Failed to update webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`) - } - } catch (e: any) { - await this.errorLog(`Failed to update webhook. Error:${e.message}`) - } - - try { - const { body, statusCode } = await request(queryWebhook, { - method: 'POST', - headers: this.generateHeaders(), - body: JSON.stringify({ - action: 'queryUrl', - }), - }) - const response: any = await body.json() - await this.debugLog(`queryWebhook: body:${JSON.stringify(response)}`) - await this.debugLog(`queryWebhook: statusCode:${statusCode}`) - if (statusCode !== 200 || response?.statusCode !== 100) { - await this.errorLog(`Failed to query webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`) - } else { - await this.infoLog(`Listening webhook on ${response?.body?.urls[0]}`) - } - } catch (e: any) { - await this.errorLog(`Failed to query webhook. Error:${e}`) + await this.errorLog(`Failed to setup webhook. Error:${e.message ?? e}`) } this.api.on('shutdown', async () => { try { - const { body, statusCode } = await request(deleteWebhook, { - method: 'POST', - headers: this.generateHeaders(), - body: JSON.stringify({ - action: 'deleteWebhook', - url, - }), - }) - const response: any = await body.json() - await this.debugLog(`deleteWebhook: url:${url}, body:${JSON.stringify(response)}, statusCode:${statusCode}`) - if (statusCode !== 200 || response?.statusCode !== 100) { - await this.errorLog(`Failed to delete webhook. HTTP:${statusCode} API:${response?.statusCode} message:${response?.message}`) - } else { - await this.infoLog('Unregistered webhook to close listening.') - } + this.switchBotAPI.deleteWebhook(url) } catch (e: any) { - await this.errorLog(`Failed to delete webhook. Error:${e.message}`) + await this.errorLog(`Failed to delete webhook. Error:${e.message ?? e}`) } }) } } async setupBlE() { + this.switchBotBLE = new SwitchBotBLE() + // Listen for log events + if (!this.config.options?.disableLogsforBLE) { + this.switchBotBLE.on('log', (log) => { + switch (log.level) { + case LogLevel.SUCCESS: + this.successLog(log.message) + break + case LogLevel.DEBUGSUCCESS: + this.debugSuccessLog(log.message) + break + case LogLevel.WARN: + this.warnLog(log.message) + break + case LogLevel.DEBUGWARN: + this.debugWarnLog(log.message) + break + case LogLevel.ERROR: + this.errorLog(log.message) + break + case LogLevel.DEBUGERROR: + this.debugErrorLog(log.message) + break + case LogLevel.DEBUG: + this.debugLog(log.message) + break + case LogLevel.INFO: + default: + this.infoLog(log.message) + } + }) + } if (this.config.options?.BLE) { await this.debugLog('setupBLE') - const switchbot = new SwitchBot() - if (switchbot === undefined) { - await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${switchbot}`) + if (this.switchBotBLE === undefined) { + await this.errorLog(`wasn't able to establish BLE Connection, node-switchbot: ${JSON.stringify(this.switchBotBLE)}`) } else { // Start to monitor advertisement packets (async () => { // Start to monitor advertisement packets await this.debugLog('Scanning for BLE SwitchBot devices...') - await switchbot.startScan() + try { + await this.switchBotBLE.startScan() + } catch (e: any) { + await this.errorLog(`Failed to start BLE scanning. Error:${e.message ?? e}`) + } // Set an event handler to monitor advertisement packets - switchbot.onadvertisement = async (ad: any) => { + this.switchBotBLE.onadvertisement = async (ad: any) => { try { this.bleEventHandler[ad.address]?.(ad.serviceData) } catch (e: any) { - await this.errorLog(`Failed to handle BLE event. Error:${e}`) + await this.errorLog(`Failed to handle BLE event. Error:${e.message ?? e}`) } } })() this.api.on('shutdown', async () => { try { - switchbot.stopScan() + // this.switchBotBLE.stopScan() await this.infoLog('Stopped BLE scanning to close listening.') } catch (e: any) { - await this.errorLog(`Failed to stop Platform BLE scanning. Error:${e.message}`) + await this.errorLog(`Failed to stop Platform BLE scanning. Error:${e.message ?? e}`) } }) } @@ -408,7 +382,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { if (!deviceConfig.deviceId) { throw new Error('The devices config section is missing the *Device ID* in the config. Please check your config.') } - if (!deviceConfig.configDeviceType && deviceConfig.connectionType) { + if (!deviceConfig.configDeviceType && (deviceConfig as devicesConfig).connectionType) { throw new Error('The devices config section is missing the *Device Type* in the config. Please check your config.') } } @@ -477,79 +451,6 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - /** - * The openToken was old config. - * This method saves the openToken as the token in the config.json file - */ - async updateToken() { - try { - // check the new token was provided - if (!this.config.credentials?.openToken) { - throw new Error('New token not provided') - } - - // load in the current config - const currentConfig = JSON.parse(readFileSync(this.api.user.configPath(), 'utf8')) - - // check the platforms section is an array before we do array things on it - if (!Array.isArray(currentConfig.platforms)) { - throw new TypeError('Cannot find platforms array in config') - } - - // find this plugins current config - const pluginConfig = currentConfig.platforms.find((x: { platform: string }) => x.platform === PLATFORM_NAME) - - if (!pluginConfig) { - throw new Error(`Cannot find config for ${PLATFORM_NAME} in platforms array`) - } - - // check the .credentials is an object before doing object things with it - if (typeof pluginConfig.credentials !== 'object') { - throw new TypeError('pluginConfig.credentials is not an object') - } - // Move openToken to token - if (!this.config.credentials.secret) { - await this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken, "openToken" cconfig has been moved to the "token" config') - this.errorLog('"secret" config is not populated, you must populate then please restart Homebridge.') - } else { - await this.warnLog('This plugin has been updated to use OpenAPI v1.1, config is set with openToken, "openToken" config has been moved to the "token" config, please restart Homebridge.') - } - - // set the refresh token - pluginConfig.credentials.token = this.config.credentials?.openToken - if (pluginConfig.credentials.token) { - pluginConfig.credentials.openToken = undefined - } - - await this.debugWarnLog(`token: ${pluginConfig.credentials.token}`) - - // save the config, ensuring we maintain pretty json - writeFileSync(this.api.user.configPath(), JSON.stringify(currentConfig, null, 4)) - await this.verifyConfig() - } catch (e: any) { - await this.errorLog(`Update Token: ${e}`) - } - } - - generateHeaders = () => { - const t = `${Date.now()}` - const nonce = randomUUID() - const data = this.config.credentials?.token + t + nonce - const signTerm = crypto - .createHmac('sha256', this.config.credentials?.secret) - .update(Buffer.from(data, 'utf-8')) - .digest() - const sign = signTerm.toString('base64') - - return { - 'Authorization': this.config.credentials?.token, - 'sign': sign, - 'nonce': nonce, - 't': t, - 'Content-Type': 'application/json', - } - } - async discoverDevices() { if (!this.config.credentials?.token) { return this.handleManualConfig() @@ -565,23 +466,20 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { while (retryCount < maxRetries) { try { - const { body, statusCode } = await request(Devices, { headers: this.generateHeaders() }) - await this.debugWarnLog(`statusCode: ${statusCode}`) - const devicesAPI: any = await body.json() - await this.debugWarnLog(`devicesAPI: ${JSON.stringify(devicesAPI)}`) - - if (this.isSuccessfulResponse(statusCode, devicesAPI.statusCode)) { - await this.handleDevices(devicesAPI.body.deviceList) - await this.handleIRDevices(devicesAPI.body.infraredRemoteList) + const { response, statusCode } = await this.switchBotAPI.getDevices() + await this.debugLog(`response: ${JSON.stringify(response)}`) + if (this.isSuccessfulResponse(statusCode)) { + await this.handleDevices(Array.isArray(response.body.deviceList) ? response.body.deviceList : []) + await this.handleIRDevices(Array.isArray(response.body.infraredRemoteList) ? response.body.infraredRemoteList : []) break } else { - await this.handleErrorResponse(statusCode, devicesAPI.statusCode, retryCount, maxRetries, delayBetweenRetries) + await this.handleErrorResponse(statusCode, retryCount, maxRetries, delayBetweenRetries) retryCount++ } } catch (e: any) { retryCount++ await this.debugErrorLog(`Failed to Discover Devices, Error Message: ${JSON.stringify(e.message)}, Submit Bugs Here: https://tinyurl.com/SwitchBotBug`) - await this.debugErrorLog(`Failed to Discover Devices, Error: ${e}`) + await this.debugErrorLog(`Failed to Discover Devices, Error: ${e.message ?? e}`) } } } @@ -608,12 +506,12 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - private isSuccessfulResponse(statusCode: number, apiStatusCode: number): boolean { - return (statusCode === 200 || statusCode === 100) && (apiStatusCode === 200 || apiStatusCode === 100) + private isSuccessfulResponse(apiStatusCode: number): boolean { + return (apiStatusCode === 200 || apiStatusCode === 100) } private async handleDevices(deviceLists: any[]) { - if (!this.config.options?.devices) { + if (!this.config.options?.devices && !this.config.options?.deviceConfig) { await this.debugLog(`SwitchBot Device Config Not Set: ${JSON.stringify(this.config.options?.devices)}`) if (deviceLists.length === 0) { await this.debugLog('SwitchBot API Has No Devices With Cloud Services Enabled') @@ -627,41 +525,83 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } } - } else { + } else if (this.config.options?.devices || this.config.options?.deviceConfig) { await this.debugLog(`SwitchBot Device Config Set: ${JSON.stringify(this.config.options?.devices)}`) - const devices = this.mergeByDeviceId(deviceLists, this.config.options.devices) - await this.debugLog(`SwitchBot Devices: ${JSON.stringify(devices)}`) - for (const device of devices) { + + // Step 1: Check and assign configDeviceType to deviceType if deviceType is not present + const devicesWithTypeConfigPromises = deviceLists.map(async (device) => { if (!device.deviceType && device.configDeviceType) { device.deviceType = device.configDeviceType await this.warnLog(`API is displaying no deviceType: ${device.deviceType}, So using configDeviceType: ${device.configDeviceType}`) } else if (!device.deviceType && !device.configDeviceName) { await this.errorLog('No deviceType or configDeviceType for device. No device will be created.') + return null // Skip this device } - if (device.deviceType) { - if (device.configDeviceName) { - device.deviceName = device.configDeviceName - } - await this.createDevice(device) + + // Retrieve deviceTypeConfig for each device and merge it + const deviceTypeConfig = this.config.options?.deviceConfig?.[device.deviceType] || {} + return Object.assign({}, device, deviceTypeConfig) + }) + + // Wait for all promises to resolve + const devicesWithTypeConfig = (await Promise.all(devicesWithTypeConfigPromises)).filter(device => device !== null) // Filter out skipped devices + + const devices = this.mergeByDeviceId(this.config.options.devices ?? [], devicesWithTypeConfig ?? []) + + await this.debugLog(`SwitchBot Devices: ${JSON.stringify(devices)}`) + + for (const device of devices) { + const deviceIdConfig = this.config.options?.devices?.[device.deviceId] || {} + const deviceWithConfig = Object.assign({}, device, deviceIdConfig) + + if (device.configDeviceName) { + device.deviceName = device.configDeviceName } + // Pass the merged device object to createDevice + await this.createDevice(deviceWithConfig) } } } private async handleIRDevices(irDeviceLists: any[]) { - if (!this.config.options?.irdevices) { + if (!this.config.options?.irdevices && !this.config.options?.irdeviceConfig) { await this.debugLog(`IR Device Config Not Set: ${JSON.stringify(this.config.options?.irdevices)}`) for (const device of irDeviceLists) { if (device.remoteType) { await this.createIRDevice(device) } } - } else { + } else if (this.config.options?.irdevices || this.config.options?.irdeviceConfig) { await this.debugLog(`IR Device Config Set: ${JSON.stringify(this.config.options?.irdevices)}`) - const devices = this.mergeByDeviceId(irDeviceLists, this.config.options.irdevices) + + // Step 1: Check and assign configRemoteType to remoteType if remoteType is not present + const devicesWithTypeConfigPromises = irDeviceLists.map(async (device) => { + if (!device.remoteType && device.configRemoteType) { + device.remoteType = device.configRemoteType + await this.warnLog(`API is displaying no remoteType: ${device.remoteType}, So using configRemoteType: ${device.configRemoteType}`) + } else if (!device.remoteType && !device.configDeviceName) { + await this.errorLog('No remoteType or configRemoteType for device. No device will be created.') + return null // Skip this device + } + + // Retrieve remoteTypeConfig for each device and merge it + const remoteTypeConfig = this.config.options?.irdeviceConfig?.[device.remoteType] || {} + return Object.assign({}, device, remoteTypeConfig) + }) + // Wait for all promises to resolve + const devicesWithRemoteTypeConfig = (await Promise.all(devicesWithTypeConfigPromises)).filter(device => device !== null) // Filter out skipped devices + + const devices = this.mergeByDeviceId(this.config.options.irdevices ?? [], devicesWithRemoteTypeConfig ?? []) + await this.debugLog(`IR Devices: ${JSON.stringify(devices)}`) for (const device of devices) { - await this.createIRDevice(device) + const irdeviceIdConfig = this.config.options?.irdevices?.[device.deviceId] || {} + const irdeviceWithConfig = Object.assign({}, device, irdeviceIdConfig) + + if (device.configDeviceName) { + device.deviceName = device.configDeviceName + } + await this.createIRDevice(irdeviceWithConfig) } } } @@ -674,11 +614,10 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { }) } - private async handleErrorResponse(statusCode: number, apiStatusCode: number, retryCount: number, maxRetries: number, delayBetweenRetries: number) { - await this.statusCode(statusCode) + private async handleErrorResponse(apiStatusCode: number, retryCount: number, maxRetries: number, delayBetweenRetries: number) { await this.statusCode(apiStatusCode) - if (statusCode === 500) { - this.infoLog(`statusCode: ${statusCode} Attempt ${retryCount + 1} of ${maxRetries}`) + if (apiStatusCode === 500) { + this.infoLog(`statusCode: ${apiStatusCode} Attempt ${retryCount + 1} of ${maxRetries}`) await sleep(delayBetweenRetries) } } @@ -707,6 +646,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { 'Smart Lock Pro': this.createLock.bind(this), 'Color Bulb': this.createColorBulb.bind(this), 'K10+': this.createRobotVacuumCleaner.bind(this), + 'K10+ Pro': this.createRobotVacuumCleaner.bind(this), 'WoSweeper': this.createRobotVacuumCleaner.bind(this), 'WoSweeperMini': this.createRobotVacuumCleaner.bind(this), 'Robot Vacuum Cleaner S1': this.createRobotVacuumCleaner.bind(this), @@ -728,9 +668,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - private async createIRDevice(device: irdevice & devicesConfig) { + private async createIRDevice(device: irdevice & irDevicesConfig) { device.connectionType = device.connectionType ?? 'OpenAPI' - const deviceTypeHandlers: { [key: string]: (device: irdevice & devicesConfig) => Promise } = { + const deviceTypeHandlers: { [key: string]: (device: irdevice & irDevicesConfig) => Promise } = { 'TV': this.createTV.bind(this), 'DIY TV': this.createTV.bind(this), 'Projector': this.createTV.bind(this), @@ -1355,14 +1295,14 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.api.updatePlatformAccessories([existingAccessory]) // create the accessory handler for the restored accessory // this is imported from `platformAccessory.ts` - new BlindTilt(this, existingAccessory, device) + new BlindTilt(this, existingAccessory, device as blindTiltConfig) await this.debugLog(`${device.deviceType} uuid: ${device.deviceId}-${device.deviceType}, (${existingAccessory.UUID})`) } else { this.unregisterPlatformAccessories(existingAccessory) } } else if (await this.registerDevice(device)) { if (isBlindTiltDevice(device)) { - if (device.group && !device.curtain?.disable_group) { + if (device.group && !(device as blindTiltConfig | curtainConfig).disable_group) { this.debugLog( 'Your Curtains are grouped, ' + `, Secondary curtain automatically hidden. Main Curtain: ${device.deviceName}, deviceId: ${device.deviceId}`, @@ -1397,7 +1337,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`) // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` - new BlindTilt(this, accessory, device) + new BlindTilt(this, accessory, device as blindTiltConfig) await this.debugLog(`${device.deviceType} uuid: ${device.deviceId}-${device.deviceType}, (${accessory.UUID})`) // publish device externally or link the accessory to your platform @@ -1432,14 +1372,14 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { this.api.updatePlatformAccessories([existingAccessory]) // create the accessory handler for the restored accessory // this is imported from `platformAccessory.ts` - new Curtain(this, existingAccessory, device) + new Curtain(this, existingAccessory, device as curtainConfig) await this.debugLog(`${device.deviceType} uuid: ${device.deviceId}-${device.deviceType}, (${existingAccessory.UUID})`) } else { this.unregisterPlatformAccessories(existingAccessory) } } else if (await this.registerDevice(device)) { if (isCurtainDevice(device)) { - if (device.group && !device.curtain?.disable_group) { + if (device.group && !(device as blindTiltConfig | curtainConfig).disable_group) { this.debugLog( 'Your Curtains are grouped, ' + `, Secondary curtain automatically hidden. Main Curtain: ${device.deviceName}, deviceId: ${device.deviceId}`, @@ -1474,7 +1414,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`) // create the accessory handler for the newly create accessory // this is imported from `platformAccessory.ts` - new Curtain(this, accessory, device) + new Curtain(this, accessory, device as curtainConfig) await this.debugLog(`${device.deviceType} uuid: ${device.deviceId}-${device.deviceType}, (${accessory.UUID})`) // publish device externally or link the accessory to your platform @@ -1947,7 +1887,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - private async createTV(device: irdevice & devicesConfig) { + private async createTV(device: irdevice & irDevicesConfig) { const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`) // see if an accessory with the same uuid has already been registered and restored from @@ -1986,7 +1926,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName) : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName) accessory.context.connectionType = await this.connectionType(device) - accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0' + accessory.context.version = device.firmware ?? this.version ?? '0.0.0' const newOrExternal = !device.external ? 'Adding new' : 'Loading external' await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`) // create the accessory handler for the newly create accessory @@ -2001,7 +1941,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - private async createIRFan(device: irdevice & devicesConfig) { + private async createIRFan(device: irdevice & irDevicesConfig) { const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`) // see if an accessory with the same uuid has already been registered and restored from @@ -2045,7 +1985,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName) : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName) accessory.context.connectionType = await this.connectionType(device) - accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0' + accessory.context.version = device.firmware ?? this.version ?? '0.0.0' const newOrExternal = !device.external ? 'Adding new' : 'Loading external' await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`) // create the accessory handler for the newly create accessory @@ -2061,7 +2001,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - private async createLight(device: irdevice & devicesConfig) { + private async createLight(device: irdevice & irDevicesConfig) { const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`) // see if an accessory with the same uuid has already been registered and restored from @@ -2105,7 +2045,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName) : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName) accessory.context.connectionType = await this.connectionType(device) - accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0' + accessory.context.version = device.firmware ?? this.version ?? '0.0.0' const newOrExternal = !device.external ? 'Adding new' : 'Loading external' await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`) // create the accessory handler for the newly create accessory @@ -2121,7 +2061,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - private async createAirConditioner(device: irdevice & devicesConfig & irDevicesConfig) { + private async createAirConditioner(device: irdevice & irDevicesConfig) { const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`) // see if an accessory with the same uuid has already been registered and restored from @@ -2165,7 +2105,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName) : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName) accessory.context.connectionType = await this.connectionType(device) - accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0' + accessory.context.version = device.firmware ?? this.version ?? '0.0.0' const newOrExternal = !device.external ? 'Adding new' : 'Loading external' await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`) // create the accessory handler for the newly create accessory @@ -2181,7 +2121,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - private async createAirPurifier(device: irdevice & devicesConfig) { + private async createAirPurifier(device: irdevice & irDevicesConfig) { const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`) // see if an accessory with the same uuid has already been registered and restored from @@ -2225,7 +2165,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName) : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName) accessory.context.connectionType = await this.connectionType(device) - accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0' + accessory.context.version = device.firmware ?? this.version ?? '0.0.0' const newOrExternal = !device.external ? 'Adding new' : 'Loading external' await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`) // create the accessory handler for the newly create accessory @@ -2241,7 +2181,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - private async createWaterHeater(device: irdevice & devicesConfig) { + private async createWaterHeater(device: irdevice & irDevicesConfig) { const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`) // see if an accessory with the same uuid has already been registered and restored from @@ -2285,7 +2225,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName) : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName) accessory.context.connectionType = await this.connectionType(device) - accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0' + accessory.context.version = device.firmware ?? this.version ?? '0.0.0' const newOrExternal = !device.external ? 'Adding new' : 'Loading external' await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`) // create the accessory handler for the newly create accessory @@ -2301,7 +2241,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - private async createVacuumCleaner(device: irdevice & devicesConfig) { + private async createVacuumCleaner(device: irdevice & irDevicesConfig) { const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`) // see if an accessory with the same uuid has already been registered and restored from @@ -2345,7 +2285,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName) : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName) accessory.context.connectionType = await this.connectionType(device) - accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0' + accessory.context.version = device.firmware ?? this.version ?? '0.0.0' const newOrExternal = !device.external ? 'Adding new' : 'Loading external' await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`) // create the accessory handler for the newly create accessory @@ -2361,7 +2301,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - private async createCamera(device: irdevice & devicesConfig) { + private async createCamera(device: irdevice & irDevicesConfig) { const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`) // see if an accessory with the same uuid has already been registered and restored from @@ -2405,7 +2345,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName) : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName) accessory.context.connectionType = await this.connectionType(device) - accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0' + accessory.context.version = device.firmware ?? this.version ?? '0.0.0' const newOrExternal = !device.external ? 'Adding new' : 'Loading external' await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`) // create the accessory handler for the newly create accessory @@ -2421,7 +2361,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - private async createOthers(device: irdevice & devicesConfig) { + private async createOthers(device: irdevice & irDevicesConfig) { const uuid = this.api.hap.uuid.generate(`${device.deviceId}-${device.remoteType}`) // see if an accessory with the same uuid has already been registered and restored from @@ -2465,7 +2405,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { ? await this.validateAndCleanDisplayName(device.configDeviceName, 'configDeviceName', device.configDeviceName) : await this.validateAndCleanDisplayName(device.deviceName, 'deviceName', device.deviceName) accessory.context.connectionType = await this.connectionType(device) - accessory.context.version = device.firmware ?? device.version ?? this.version ?? '0.0.0' + accessory.context.version = device.firmware ?? this.version ?? '0.0.0' const newOrExternal = !device.external ? 'Adding new' : 'Loading external' await this.infoLog(`${newOrExternal} accessory: ${accessory.displayName} deviceId: ${device.deviceId}`) // create the accessory handler for the newly create accessory @@ -2484,10 +2424,10 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { async registerCurtains(device: device & devicesConfig): Promise { let registerWindowCovering: boolean if (isCurtainDevice(device)) { - await this.debugWarnLog(`deviceName: ${device.deviceName} deviceId: ${device.deviceId}, curtainDevicesIds: ${device.curtainDevicesIds},x master: ${device.master}, group: ${device.group}, disable_group: ${device.curtain?.disable_group}, connectionType: ${device.connectionType}`) + await this.debugWarnLog(`deviceName: ${device.deviceName} deviceId: ${device.deviceId}, curtainDevicesIds: ${device.curtainDevicesIds},x master: ${device.master}, group: ${device.group}, disable_group: ${(device as blindTiltConfig | curtainConfig).disable_group}, connectionType: ${device.connectionType}`) registerWindowCovering = await this.registerWindowCovering(device) } else if (isBlindTiltDevice(device)) { - await this.debugWarnLog(`deviceName: ${device.deviceName} deviceId: ${device.deviceId}, blindTiltDevicesIds: ${device.blindTiltDevicesIds}, master: ${device.master}, group: ${device.group}, disable_group: ${device.curtain?.disable_group}, connectionType: ${device.connectionType}`) + await this.debugWarnLog(`deviceName: ${device.deviceName} deviceId: ${device.deviceId}, blindTiltDevicesIds: ${device.blindTiltDevicesIds}, master: ${device.master}, group: ${device.group}, disable_group: ${(device as blindTiltConfig | curtainConfig).disable_group}, connectionType: ${device.connectionType}`) registerWindowCovering = await this.registerWindowCovering(device) } else { registerWindowCovering = false @@ -2495,7 +2435,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { return registerWindowCovering } - async registerWindowCovering(device: ((curtain | curtain3) & devicesConfig) | (blindTilt & devicesConfig)) { + async registerWindowCovering(device: (curtain | curtain3 | blindTilt) & devicesConfig) { await this.debugLog(`master: ${device.master}`) let registerCurtain: boolean if (device.master && device.group) { @@ -2503,11 +2443,9 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { registerCurtain = true await this.debugLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] device.master: ${device.master}, device.group: ${device.group} connectionType; ${device.connectionType}`) await this.debugWarnLog(`Device: ${device.deviceName} registerCurtains: ${registerCurtain}`) - } else if (!device.master && device.curtain?.disable_group) { - // !device.group && device.connectionType === 'BLE' - // OpenAPI: Non-Master Curtains/Blind Tilts that has Disable Grouping Checked + } else if (!device.master && (device as blindTiltConfig | curtainConfig).disable_group) { registerCurtain = true - await this.debugLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] device.master: ${device.master}, disable_group: ${device.curtain?.disable_group}, connectionType; ${device.connectionType}`) + await this.debugLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] device.master: ${device.master}, disable_group: ${(device as blindTiltConfig | curtainConfig).disable_group}, connectionType; ${device.connectionType}`) await this.debugWarnLog(`Device: ${device.deviceName} registerCurtains: ${registerCurtain}`) } else if (device.master && !device.group) { // OpenAPI: Master Curtains/Blind Tilts not in Group @@ -2521,13 +2459,13 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { await this.debugWarnLog(`Device: ${device.deviceName} registerCurtains: ${registerCurtain}`) } else { registerCurtain = false - await this.debugErrorLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] disable_group: ${device.curtain?.disable_group}, device.master: ${device.master}, device.group: ${device.group}`) + await this.debugErrorLog(`deviceName: ${device.deviceName} [${device.deviceType} Config] disable_group: ${(device as blindTiltConfig | curtainConfig).disable_group}, device.master: ${device.master}, device.group: ${device.group}`) await this.debugWarnLog(`Device: ${device.deviceName} registerCurtains: ${registerCurtain}, device.connectionType: ${device.connectionType}`) } return registerCurtain } - async connectionType(device: device & devicesConfig): Promise { + async connectionType(device: (device & devicesConfig) | (irdevice & irDevicesConfig)): Promise { let connectionType: string if (!device.connectionType && this.config.credentials?.token && this.config.credentials.secret) { connectionType = 'OpenAPI' @@ -2577,7 +2515,7 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { return registerDevice } - public async externalOrPlatform(device: device & (irDevicesConfig | devicesConfig), accessory: PlatformAccessory) { + public async externalOrPlatform(device: (device & devicesConfig) | (irdevice & irDevicesConfig), accessory: PlatformAccessory) { const { displayName } = accessory const isExternal = device.external ?? false @@ -2639,18 +2577,15 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { } } - async retryRequest(deviceMaxRetries: number, deviceDelayBetweenRetries: number, url: string | URL | UrlObject, options?: { dispatcher?: Dispatcher } & Omit & Partial>): Promise<{ body: any, statusCode: number }> { + async retryRequest(deviceId: string, deviceMaxRetries: number, deviceDelayBetweenRetries: number): Promise<{ response: deviceStatus, statusCode: deviceStatusRequest['statusCode'] }> { let retryCount = 0 const maxRetries = deviceMaxRetries const delayBetweenRetries = deviceDelayBetweenRetries while (retryCount < maxRetries) { try { - const { body, statusCode } = await request(url, options) - if (statusCode === 200 || statusCode === 100) { - return { body, statusCode } - } else { - await this.debugLog(`Received status code: ${statusCode}`) - } + const { response, statusCode } = await this.switchBotAPI.getDeviceStatus(deviceId) + await this.debugLog(`response: ${JSON.stringify(response)}`) + return { response, statusCode } } catch (error: any) { await this.errorLog(`Error making request: ${error.message}`) } @@ -2658,18 +2593,24 @@ export class SwitchBotPlatform implements DynamicPlatformPlugin { await this.debugLog(`Retry attempt ${retryCount} of ${maxRetries}`) await sleep(delayBetweenRetries) } - return { body: null, statusCode: -1 } + return { response: { + deviceId: '', + deviceType: '', + hubDeviceId: '', + version: 0, + deviceName: '', + enableCloudService: false, + }, statusCode: 500 } } // BLE Connection async connectBLE(accessory: PlatformAccessory, device: device & devicesConfig): Promise { try { - const switchbot = new SwitchBot() - queueScheduler.schedule(async () => switchbot) - await this.debugLog(`${device.deviceType}: ${accessory.displayName} 'node-switchbot' found: ${switchbot}`) - return switchbot + queueScheduler.schedule(async () => this.switchBotBLE) + await this.debugLog(`${device.deviceType}: ${accessory.displayName} 'node-switchbot' found: ${this.switchBotBLE}`) + return this.switchBotBLE } catch (e: any) { - await this.errorLog(`${device.deviceType}: ${accessory.displayName} 'node-switchbot' not found, Error: ${e}`) + await this.errorLog(`${device.deviceType}: ${accessory.displayName} 'node-switchbot' not found, Error: ${e.message ?? e}`) return false } } diff --git a/src/settings.ts b/src/settings.ts index a42172ef..05f8e2cd 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -4,10 +4,11 @@ */ import type { IClientOptions } from 'async-mqtt' import type { PlatformConfig } from 'homebridge' -import type { SwitchBotBLEModel, SwitchBotBLEModelFriendlyName, SwitchBotBLEModelName } from 'node-switchbot' - -import type { device } from './types/devicelist' -import type { irdevice } from './types/irdevicelist' +/* +* For Testing Locally: +* import type { device, irdevice, SwitchBotBLEModel, SwitchBotBLEModelFriendlyName, SwitchBotBLEModelName } from '/Users/Shared/GitHub/OpenWonderLabs/node-switchbot/dist/index.js'; +*/ +import type { device, irdevice, SwitchBotBLEModel, SwitchBotBLEModelFriendlyName, SwitchBotBLEModelName } from 'node-switchbot' /** * This is the name of the platform that users will use to register the plugin in the Homebridge config.json */ @@ -18,51 +19,31 @@ export const PLATFORM_NAME = 'SwitchBot' */ export const PLUGIN_NAME = '@switchbot/homebridge-switchbot' -/** - * This is the main url used to access SwitchBot API - */ -export const Devices = 'https://api.switch-bot.com/v1.1/devices' - -/** - * This is the updateWebhook url used to access SwitchBot API - */ -export const setupWebhook = 'https://api.switch-bot.com/v1.1/webhook/setupWebhook' - -/** - * This is the updateWebhook url used to access SwitchBot API - */ -export const queryWebhook = 'https://api.switch-bot.com/v1.1/webhook/queryWebhook' - -/** - * This is the updateWebhook url used to access SwitchBot API - */ -export const updateWebhook = 'https://api.switch-bot.com/v1.1/webhook/updateWebhook' - -/** - * This is the deleteWebhook url used to access SwitchBot API - */ -export const deleteWebhook = 'https://api.switch-bot.com/v1.1/webhook/deleteWebhook' - // Config export interface SwitchBotPlatformConfig extends PlatformConfig { credentials?: credentials options?: options + deviceConfig?: { [deviceType: string]: devicesConfig } } interface credentials { - token?: any - secret?: any - notice?: any - openToken?: any + token?: string + secret?: string + notice?: string } export interface options { devices?: devicesConfig[] + deviceConfig?: { [deviceType: string]: devicesConfig } irdevices?: irDevicesConfig[] + irdeviceConfig?: { [remoteType: string]: irDevicesConfig } allowInvalidCharacters?: boolean mqttURL?: string mqttOptions?: IClientOptions mqttPubOptions?: IClientOptions BLE?: boolean + discoverBLE?: boolean + disableLogsforBLE?: boolean + disableLogsforOpenAPI?: boolean webhookURL?: string maxRetries?: number delayBetweenRetries?: number @@ -72,7 +53,9 @@ export interface options { logging?: string }; -export interface devicesConfig extends device { +export type devicesConfig = botConfig | meterConfig | indoorOutdoorSensorConfig | humidifierConfig | curtainConfig | blindTiltConfig | contactConfig | motionConfig | waterDetectorConfig | plugConfig | colorBulbConfig | stripLightConfig | ceilingLightConfig | lockConfig | hubConfig + +export interface BaseDeviceConfig extends device { bleMac?: string model: string bleModel: SwitchBotBLEModel @@ -101,50 +84,41 @@ export interface devicesConfig extends device { mqttPubOptions?: IClientOptions history?: boolean webhook?: boolean - bot?: bot - meter?: meter - iosensor?: iosensor - humidifier?: humidifier - curtain?: curtain - blindTilt?: blindTilt - contact?: contact - motion?: motion - waterdetector?: waterdetector - colorbulb?: colorbulb - striplight?: striplight - ceilinglight?: ceilinglight - plug?: plug - lock?: lock - hub?: hub } -interface meter { +export interface botConfig extends BaseDeviceConfig { + configDeviceType: 'Bot' + mode?: string + type: string + doublePress?: number + pushRatePress?: number + allowPush?: boolean + multiPress?: boolean +}; + +export interface meterConfig extends BaseDeviceConfig { + configDeviceType: 'Meter' | 'MeterPlus' hide_temperature?: boolean convertUnitTo?: string hide_humidity?: boolean }; -interface iosensor { +export interface indoorOutdoorSensorConfig extends BaseDeviceConfig { + configDeviceType: 'WoIOSensor' hide_temperature?: boolean convertUnitTo?: string hide_humidity?: boolean }; -interface bot { - mode?: string - deviceType?: string - doublePress?: number - pushRatePress?: number - allowPush?: boolean - multiPress?: boolean -}; - -interface humidifier { +export interface humidifierConfig extends BaseDeviceConfig { + configDeviceType: 'Humidifier' hide_temperature?: boolean + convertUnitTo?: string set_minStep?: number }; -interface curtain { +export interface curtainConfig extends BaseDeviceConfig { + configDeviceType: 'Curtain' | 'Curtain3' | 'WoRollerShade' | 'Roller Shade' disable_group?: boolean hide_lightsensor?: boolean set_minLux?: number @@ -157,8 +131,10 @@ interface curtain { silentModeSwitch?: boolean }; -interface blindTilt { - mode?: string +export interface blindTiltConfig extends BaseDeviceConfig { + configDeviceType: 'Blind Tilt' + disable_group?: boolean + mapping?: string hide_lightsensor?: boolean set_minLux?: number set_maxLux?: number @@ -170,54 +146,68 @@ interface blindTilt { silentModeSwitch?: boolean }; -interface contact { +export interface contactConfig extends BaseDeviceConfig { + configDeviceType: 'Contact Sensor' hide_lightsensor?: boolean set_minLux?: number set_maxLux?: number hide_motionsensor?: boolean }; -interface motion { +export interface motionConfig extends BaseDeviceConfig { + configDeviceType: 'Motion Sensor' hide_lightsensor?: boolean set_minLux?: number set_maxLux?: number }; -interface waterdetector { +export interface waterDetectorConfig extends BaseDeviceConfig { + configDeviceType: 'Water Detector' hide_leak?: boolean dry?: boolean }; -interface colorbulb { +export interface plugConfig extends BaseDeviceConfig { + configDeviceType: 'Plug' | 'Plug Mini (US)' | 'Plug Mini (JP)' +}; + +export interface colorBulbConfig extends BaseDeviceConfig { + configDeviceType: 'Color Bulb' set_minStep?: number adaptiveLightingShift?: number }; -interface striplight { +export interface stripLightConfig extends BaseDeviceConfig { + configDeviceType: 'Strip Light' set_minStep?: number adaptiveLightingShift?: number }; -interface ceilinglight { +export interface ceilingLightConfig extends BaseDeviceConfig { + configDeviceType: 'Ceiling Light' | 'Ceiling Light Pro' set_minStep?: number adaptiveLightingShift?: number }; -type plug = object - -interface lock { +export interface lockConfig extends BaseDeviceConfig { + configDeviceType: 'Smart Lock' | 'Smart Lock Pro' hide_contactsensor?: boolean activate_latchbutton?: boolean }; -interface hub { +export interface hubConfig extends BaseDeviceConfig { + configDeviceType: 'Hub 2' hide_temperature?: boolean convertUnitTo?: string hide_humidity?: boolean hide_lightsensor?: boolean + set_minLux?: number + set_maxLux?: number }; -export interface irDevicesConfig extends irdevice { +export type irDevicesConfig = irFanConfig | irLightConfig | irAirConfig | irOtherConfig + +export interface irBaseDeviceConfig extends irdevice { configDeviceName?: string configRemoteType?: string connectionType?: string @@ -233,18 +223,10 @@ export interface irDevicesConfig extends irdevice { disablePushOn?: boolean disablePushOff?: boolean disablePushDetail?: boolean - irfan?: irfan - irair?: irair - irpur?: Record - ircam?: Record - irlight?: irlight - irvc?: Record - irwh?: Record - irtv?: Record - other?: other } -interface irfan { +export interface irFanConfig extends irBaseDeviceConfig { + configRemoteType?: 'Fan' | 'DIY Fan' swing_mode?: boolean rotation_speed?: boolean set_minStep?: number @@ -252,11 +234,13 @@ interface irfan { set_min?: number }; -interface irlight { +export interface irLightConfig extends irBaseDeviceConfig { + configRemoteType?: 'Light' | 'DIY Light' stateless?: boolean }; -interface irair { +export interface irAirConfig extends irBaseDeviceConfig { + configRemoteType?: 'Air Conditioner' | 'DIY Air Conditioner' hide_automode?: boolean set_max_heat?: number set_min_heat?: number @@ -267,6 +251,7 @@ interface irair { meterUuid?: string }; -interface other { - deviceType?: string +export interface irOtherConfig extends irBaseDeviceConfig { + configRemoteType?: 'Others' + type?: string }; diff --git a/src/types/bledevicestatus.ts b/src/types/bledevicestatus.ts deleted file mode 100644 index 440387a5..00000000 --- a/src/types/bledevicestatus.ts +++ /dev/null @@ -1,303 +0,0 @@ -/* Copyright(C) 2017-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. - * - * bledevicestatus.ts: @switchbot/homebridge-switchbot platform class. - */ -import type { MacAddress } from 'homebridge' -import type { SwitchBotBLEModel, SwitchBotBLEModelFriendlyName, SwitchBotBLEModelName } from 'node-switchbot' - -export interface switchbot { - discover: (arg0: { duration?: any, model: string, quick: boolean, id?: MacAddress }) => Promise - wait: (arg0: number) => any -} - -export interface ad { - id: string - address: string - rssi: number - serviceData: botServiceData | colorBulbServiceData | contactSensorServiceData | curtainServiceData | curtain3ServiceData | stripLightServiceData | lockServiceData | lockProServiceData | meterServiceData | meterPlusServiceData | motionSensorServiceData | outdoorMeterServiceData | plugMiniUSServiceData | plugMiniJPServiceData | blindTiltServiceData | ceilingLightServiceData | ceilingLightProServiceData | hub2ServiceData | batteryCirculatorFanServiceData | waterLeakDetectorServiceData | humidifierServiceData | robotVacuumCleanerServiceData -} - -interface serviceData { - model: string - modelName: string -} - -export type botServiceData = serviceData & { - model: SwitchBotBLEModel.Bot - modelName: SwitchBotBLEModelName.Bot - modelFriendlyName: SwitchBotBLEModelFriendlyName.Bot - mode: string - state: boolean - battery: number -} - -export type colorBulbServiceData = serviceData & { - model: SwitchBotBLEModel.ColorBulb - modelName: SwitchBotBLEModelName.ColorBulb - modelFriendlyName: SwitchBotBLEModelFriendlyName.ColorBulb - color_temperature: number - power: boolean - state: boolean - red: number - green: number - blue: number - brightness: number - delay: number - preset: number - color_mode: number - speed: number - loop_index: number -} - -export type contactSensorServiceData = serviceData & { - model: SwitchBotBLEModel.ContactSensor - modelName: SwitchBotBLEModelName.ContactSensor - modelFriendlyName: SwitchBotBLEModelFriendlyName.ContactSensor - movement: boolean - tested: boolean - battery: number - contact_open: boolean - contact_timeout: boolean - lightLevel: string - button_count: number - doorState: string -} - -export type curtainServiceData = serviceData & { - model: SwitchBotBLEModel.Curtain - modelName: SwitchBotBLEModelName.Curtain - modelFriendlyName: SwitchBotBLEModelFriendlyName.Curtain - calibration: boolean - battery: number - inMotion: boolean - position: number - lightLevel: number - deviceChain: number -} - -export type curtain3ServiceData = serviceData & { - model: SwitchBotBLEModel.Curtain3 - modelName: SwitchBotBLEModelName.Curtain3 - modelFriendlyName: SwitchBotBLEModelFriendlyName.Curtain3 - calibration: boolean - battery: number - inMotion: boolean - position: number - lightLevel: number - deviceChain: number -} - -export type stripLightServiceData = serviceData & { - model: SwitchBotBLEModel.StripLight - modelName: SwitchBotBLEModelName.StripLight - modelFriendlyName: SwitchBotBLEModelFriendlyName.StripLight - power: boolean - state: boolean - red: number - green: number - blue: number - brightness: number - delay: number - preset: number - color_mode: number - speed: number - loop_index: number -} - -export type lockServiceData = serviceData & { - model: SwitchBotBLEModel.Lock - modelName: SwitchBotBLEModelName.Lock - modelFriendlyName: SwitchBotBLEModelFriendlyName.Lock - battery: number - calibration: boolean - status: string - update_from_secondary_lock: boolean - door_open: string - double_lock_mode: boolean - unclosed_alarm: boolean - unlocked_alarm: boolean - auto_lock_paused: boolean - night_latch: boolean -} - -export type lockProServiceData = serviceData & { - model: SwitchBotBLEModel.LockPro - modelName: SwitchBotBLEModelName.LockPro - modelFriendlyName: SwitchBotBLEModelFriendlyName.LockPro - battery: number - calibration: boolean - status: string - update_from_secondary_lock: boolean - door_open: string - double_lock_mode: boolean - unclosed_alarm: boolean - unlocked_alarm: boolean - auto_lock_paused: boolean - night_latch: boolean -} - -export type meterServiceData = serviceData & { - model: SwitchBotBLEModel.Meter - modelName: SwitchBotBLEModelName.Meter - modelFriendlyName: SwitchBotBLEModelFriendlyName.Meter - celsius: number - fahrenheit: number - fahrenheit_mode: boolean - humidity: number - battery: number -} - -export type meterPlusServiceData = serviceData & { - model: SwitchBotBLEModel.MeterPlus - modelName: SwitchBotBLEModelName.MeterPlus - modelFriendlyName: SwitchBotBLEModelFriendlyName.MeterPlus - celsius: number - fahrenheit: number - fahrenheit_mode: boolean - humidity: number - battery: number -} - -export type outdoorMeterServiceData = serviceData & { - model: SwitchBotBLEModel.OutdoorMeter - modelName: SwitchBotBLEModelName.OutdoorMeter - modelFriendlyName: SwitchBotBLEModelFriendlyName.OutdoorMeter - celsius: number - fahrenheit: number - fahrenheit_mode: boolean - humidity: number - battery: number -} - -export type motionSensorServiceData = serviceData & { - model: SwitchBotBLEModel.MotionSensor - modelName: SwitchBotBLEModelName.MotionSensor - modelFriendlyName: SwitchBotBLEModelFriendlyName.MotionSensor - tested: boolean - movement: boolean - battery: number - led: number - iot: number - sense_distance: number - lightLevel: string - is_light: boolean -} - -export type plugMiniUSServiceData = serviceData & { - model: SwitchBotBLEModel.PlugMiniUS - modelName: SwitchBotBLEModelName.PlugMini - modelFriendlyName: SwitchBotBLEModelFriendlyName.PlugMini - state: string - delay: boolean - timer: boolean - syncUtcTime: boolean - wifiRssi: number - overload: boolean - currentPower: number -} - -export type plugMiniJPServiceData = serviceData & { - model: SwitchBotBLEModel.PlugMiniUS - modelName: SwitchBotBLEModelName.PlugMini - modelFriendlyName: SwitchBotBLEModelFriendlyName.PlugMini - state: string - delay: boolean - timer: boolean - syncUtcTime: boolean - wifiRssi: number - overload: boolean - currentPower: number -} - -export type blindTiltServiceData = serviceData & { - model: SwitchBotBLEModel.BlindTilt - modelName: SwitchBotBLEModelName.BlindTilt - modelFriendlyName: SwitchBotBLEModelFriendlyName.BlindTilt - calibration: boolean - battery: number - inMotion: boolean - tilt: number - lightLevel: number -} - -export type ceilingLightServiceData = serviceData & { - model: SwitchBotBLEModel.CeilingLight - modelName: SwitchBotBLEModelName.CeilingLight - modelFriendlyName: SwitchBotBLEModelFriendlyName.CeilingLight - color_temperature: number - power: boolean - state: boolean - red: number - green: number - blue: number - brightness: number - delay: number - preset: number - color_mode: number - speed: number - loop_index: number -} - -export type ceilingLightProServiceData = serviceData & { - model: SwitchBotBLEModel.CeilingLightPro - modelName: SwitchBotBLEModelName.CeilingLightPro - modelFriendlyName: SwitchBotBLEModelFriendlyName.CeilingLightPro - color_temperature: number - power: boolean - state: boolean - red: number - green: number - blue: number - brightness: number - delay: number - preset: number - color_mode: number - speed: number - loop_index: number -} - -export type hub2ServiceData = serviceData & { - model: SwitchBotBLEModel.Hub2 - modelName: SwitchBotBLEModelName.Hub2 - modelFriendlyName: SwitchBotBLEModelFriendlyName.Hub2 - celsius: number - fahrenheit: number - fahrenheit_mode: boolean - humidity: number - lightLevel: number -} - -export type batteryCirculatorFanServiceData = serviceData & { - model: SwitchBotBLEModel.Unknown - modelName: SwitchBotBLEModelName.Unknown - modelFriendlyName: SwitchBotBLEModelFriendlyName.Unknown - state: string - fanSpeed: number -} - -export type waterLeakDetectorServiceData = serviceData & { - model: SwitchBotBLEModel.Unknown - modelName: SwitchBotBLEModelName.Unknown - modelFriendlyName: SwitchBotBLEModelFriendlyName.Unknown - state: boolean - status: number - battery: number -} - -export type humidifierServiceData = serviceData & { - model: SwitchBotBLEModel.Humidifier - modelName: SwitchBotBLEModelName.Humidifier - modelFriendlyName: SwitchBotBLEModelFriendlyName.Humidifier - onState: boolean - autoMode: boolean - percentage: number - humidity: number -} - -export type robotVacuumCleanerServiceData = serviceData & { - model: SwitchBotBLEModel.Unknown - modelName: SwitchBotBLEModelName.Unknown - modelFriendlyName: SwitchBotBLEModelFriendlyName.Unknown - state: string - battery: number -} diff --git a/src/types/devicelist.ts b/src/types/devicelist.ts deleted file mode 100644 index 41d65f8c..00000000 --- a/src/types/devicelist.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* Copyright(C) 2017-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. - * - * devicelist.ts: @switchbot/homebridge-switchbot platform class. - */ - -export interface deviceList { - device: device[] -} - -export interface device { - deviceId: string - deviceName: string - deviceType: string - enableCloudService: boolean - hubDeviceId: string - version?: number -} - -export type bot = device & {} - -export type curtain = device & { - curtainDevicesIds: string[] - calibrate: boolean - group: boolean - master: boolean - openDirection: string -} - -export type curtain3 = device & { - curtainDevicesIds: string[] - calibrate: boolean - group: boolean - master: boolean - openDirection?: string -} - -export type hub2 = device & {} - -export type meter = device & {} - -export type meterPlus = device & {} - -export type outdoorMeter = device & {} - -export type lock = device & { - group: boolean - master: boolean - groupName: string - lockDevicesIds: string[] -} - -export type lockPro = device & { - group: boolean - master: boolean - groupName: string - lockDevicesIds: string[] -} - -export type keypad = device & { - remoteType: string - lockDeviceId: string - keyList: keyList -} - -export type keypadTouch = device & { - remoteType: string - lockDeviceId: string - keyList: keyList -} - -interface keyList { - id: number - name: string - type: string - password: string - iv: string - status: string - createTime: number -} - -export type remote = device & {} - -export type motionSensor = device & {} - -export type contactSensor = device & {} - -export type waterLeakDetector = device & {} - -export type ceilingLight = device & {} - -export type ceilingLightPro = device & {} - -export type plug = device & {} - -export type plugMini = device & {} - -export type stripLight = device & {} - -export type colorBulb = device & {} - -export type robotVacuumCleanerS1 = device & {} - -export type robotVacuumCleanerS1Plus = device & {} - -export type floorCleaningRobotS10 = device & {} - -export type humidifier = device & {} - -export type indoorCam = device & {} - -export type pantiltCam = device & {} - -export type pantiltCam2k = device & {} - -export type blindTilt = device & { - blindTiltDevicesIds: string[] - calibrate: boolean - group: boolean - master: boolean - direction: string - slidePosition: number -} - -export type batteryCirculatorFan = device & {} diff --git a/src/types/deviceresponse.ts b/src/types/deviceresponse.ts deleted file mode 100644 index 080f8b8b..00000000 --- a/src/types/deviceresponse.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { deviceList } from './devicelist' -import type { infraredRemoteList } from './irdevicelist' - -// json response from SwitchBot API -export interface devices { - statusCode: 100 | 190 | 'n/a' - message: string - body: body -} - -interface body { - deviceList: deviceList - infraredRemoteList: infraredRemoteList -} diff --git a/src/types/devicestatus.ts b/src/types/devicestatus.ts deleted file mode 100644 index c8a1be46..00000000 --- a/src/types/devicestatus.ts +++ /dev/null @@ -1,178 +0,0 @@ -import type { device } from './devicelist' - -/* Copyright(C) 2017-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. - * - * devicestatus.ts: @switchbot/homebridge-switchbot platform class. - */ -export interface deviceStatusRequest { - statusCode: number - message: string - body: deviceStatus -} - -export interface deviceStatus extends device { - // properties on all devices - deviceId: string - deviceType: string - hubDeviceId: string - version: number -}; - -export type botStatus = deviceStatus & { - power: string - battery: number - mode: 'pressMode' | 'switchMode' | 'customizeMode' -} - -export type curtainStatus = deviceStatus & { - calibrate: boolean - group: boolean - moving: boolean - battery: number - slidePosition: number - lightLevel?: 'bright' | 'dim' -} - -export type meterStatus = deviceStatus & { - temperature: number - battery: number - humidity: number -} - -export type meterPlusStatus = deviceStatus & { - temperature: number - battery: number - humidity: number -} - -export type outdoorMeterStatus = deviceStatus & { - battery: number - temperature: number - humidity: number -} - -export type lockStatus = deviceStatus & { - lockState: string - doorState: string - moveDetected: boolean - battery: number -} - -export type lockProStatus = deviceStatus & { - lockState: string - doorState: string - moveDetected: boolean - battery: number -} - -export type motionSensorStatus = deviceStatus & { - battery: number - moveDetected: boolean - brightness: 'bright' | 'dim' -} - -export type contactSensorStatus = deviceStatus & { - battery: number - moveDetected: boolean - openState: 'open' | 'close' | 'timeOutNotClose' - brightness: 'bright' | 'dim' -} - -export type waterLeakDetectorStatus = deviceStatus & { - battery: number - status: 0 /* dry */ | 1 /* leak detected */ -} - -export type ceilingLightStatus = deviceStatus & { - power: boolean - brightness: number - colorTemperature: number -} - -export type ceilingLightProStatus = deviceStatus & { - power: boolean - brightness: number - colorTemperature: number -} - -export type plugStatus = deviceStatus & { - power: string - version: string -} - -export type plugMiniStatus = deviceStatus & { - voltage: Float64Array - weight: Float64Array - electricityOfDay: number - electricCurrent: Float64Array - power: string -} - -export type stripLightStatus = deviceStatus & { - power: string - brightness: number - color: string -} - -export type colorBulbStatus = deviceStatus & { - power: string - brightness: number - color: string - colorTemperature: number -} - -export type robotVacuumCleanerS1Status = deviceStatus & { - workingStatus: string - onlineStatus: string - battery: number -} - -export type robotVacuumCleanerS1PlusStatus = deviceStatus & { - workingStatus: string - onlineStatus: string - battery: number -} - -export type floorCleaningRobotS10Status = deviceStatus & { - workingStatus: string - onlineStatus: string - battery: number - waterBaseBattery: number - taskType: string -} - -export type humidifierStatus = deviceStatus & { - power: string - humidity: number - temperature: number - nebulizationEfficiency: number - auto: boolean - childLock: boolean - sound: boolean - lackWater: boolean -} - -export type blindTiltStatus = deviceStatus & { - calibrate: boolean - battery: number - direction: string - slidePosition: string - lightLevel?: 'bright' | 'dim' -} - -export type hub2Status = deviceStatus & { - temperature: number - lightLevel: number - humidity: number -} - -export type batteryCirculatorFanStatus = deviceStatus & { - mode: 'direct' | 'natural' | 'sleep' | 'baby' - battery: number - power: string - nightStatus: number - oscillation: string - verticalOscillation: string - chargingStatus: string - fanSpeed: number -} diff --git a/src/types/devicewebhookstatus.ts b/src/types/devicewebhookstatus.ts deleted file mode 100644 index 89194c08..00000000 --- a/src/types/devicewebhookstatus.ts +++ /dev/null @@ -1,191 +0,0 @@ -/* Copyright(C) 2017-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. - * - * devicestatus.ts: @switchbot/homebridge-switchbot platform class. - */ -interface deviceWebhook { - eventType: string - eventVersion: string - context: deviceWebhookContext -} - -export { deviceWebhook } - -export interface deviceWebhookContext { - // properties on all devices - deviceMac: string - deviceType: string - timeOfSample: number -} - -export type botWebhookContext = deviceWebhookContext & { - power: string // "on"or"off" - battery: number - deviceMode: 'pressMode' | 'switchMode' | 'customizeMode' -} - -export type curtainWebhookContext = deviceWebhookContext & { - calibrate: boolean - group: boolean - slidePosition: number // 0~100 - battery: number -} - -export type curtain3WebhookContext = deviceWebhookContext & { - calibrate: boolean - group: boolean - slidePosition: number // 0~100 - battery: number -} - -export type motionSensorWebhookContext = deviceWebhookContext & { - detectionState: 'NOT_DETECTED' | 'DETECTED' -} - -export type contactSensorWebhookContext = deviceWebhookContext & { - detectionState: 'NOT_DETECTED' | 'DETECTED' - doorMode: 'IN_DOOR' | 'OUT_DOOR' - brightness: 'dim' | 'bright' - openState: 'open' | 'close' | 'timeOutNotClose' -} - -export type waterLeakDetectorWebhookContext = deviceWebhookContext & { - detectionState: 0 | 1 - battery: number // 0~100 -} - -export type meterWebhookContext = deviceWebhookContext & { - temperature: number - scale: 'CELSIUS' | 'FAHRENHEIT' - humidity: number -} - -export type meterPlusWebhookContext = deviceWebhookContext & { - temperature: number - scale: 'CELSIUS' | 'FAHRENHEIT' - humidity: number -} - -export type outdoorMeterWebhookContext = deviceWebhookContext & { - temperature: number - scale: 'CELSIUS' | 'FAHRENHEIT' - humidity: number -} - -export type lockWebhookContext = deviceWebhookContext & { - lockState: 'UNLOCKED' | 'LOCKED' | 'JAMMED' -} - -export type lockProWebhookContext = deviceWebhookContext & { - lockState: 'UNLOCKED' | 'LOCKED' | 'JAMMED' -} - -export type indoorCameraWebhookContext = deviceWebhookContext & { - detectionState: 'DETECTED' -} - -export type panTiltCamWebhookContext = deviceWebhookContext & { - detectionState: 'DETECTED' -} - -export type colorBulbWebhookContext = deviceWebhookContext & { - powerState: 'ON' | 'OFF' - brightness: number - color: string // RGB 255:255:255 - colorTemperature: number // 2700~6500 -} - -export type stripLightWebhookContext = deviceWebhookContext & { - powerState: 'ON' | 'OFF' - brightness: number - color: string // RGB 255:255:255 -} - -export type plugWebhookContext = deviceWebhookContext & { - powerState: 'ON' | 'OFF' -} - -export type plugMiniUSWebhookContext = deviceWebhookContext & { - powerState: 'ON' | 'OFF' -} - -export type plugMiniJPWebhookContext = deviceWebhookContext & { - powerState: 'ON' | 'OFF' -} - -export type robotVacuumCleanerS1WebhookContext = deviceWebhookContext & { - workingStatus: 'Standby' | 'Clearing' | 'Paused' | 'GotoChargeBase' | 'Charging' | 'ChargeDone' | 'Dormant' | 'InTrouble' | 'InRemoteControl' | 'InDustCollecting' - onlineStatus: 'online' | 'offline' - battery: number // 0~100 -} - -export type robotVacuumCleanerS1PlusWebhookContext = deviceWebhookContext & { - workingStatus: 'Standby' | 'Clearing' | 'Paused' | 'GotoChargeBase' | 'Charging' | 'ChargeDone' | 'Dormant' | 'InTrouble' | 'InRemoteControl' | 'InDustCollecting' - onlineStatus: 'online' | 'offline' - battery: number // 0~100 -} - -export type floorCleaningRobotS10WebhookContext = deviceWebhookContext & { - workingStatus: 'Standby' | 'Clearing' | 'Paused' | 'GotoChargeBase' | 'Charging' | 'ChargeDone' | 'Dormant' | 'InTrouble' | 'InRemoteControl' | 'InDustCollecting' - onlineStatus: 'online' | 'offline' - battery: number // 0~100 - waterBaseBattery: number // 0~100 - taskType: 'standBy' | 'explore' | 'cleanAll' | 'cleanArea' | 'cleanRoom' | 'fillWater' | 'deepWashing' | 'backToCharge' | 'markingWaterBase' | 'drying' | 'collectDust' | 'remoteControl' | 'cleanWithExplorer' | 'fillWaterForHumi' | 'markingHumi' -} - -export type ceilingLightWebhookContext = deviceWebhookContext & { - powerState: 'ON' | 'OFF' - brightness: number - colorTemperature: number // 2700~6500 -} - -export type ceilingLightProWebhookContext = deviceWebhookContext & { - powerState: 'ON' | 'OFF' - brightness: number - colorTemperature: number // 2700~6500 -} - -export type keypadWebhookContext = deviceWebhookContext & { - eventName: 'createKey' | 'deleteKey' - commandId: string - result: 'success' | 'failed' | 'timeout' -} - -export type keypadTouchWebhookContext = deviceWebhookContext & { - eventName: 'createKey' | 'deleteKey' - commandId: string - result: 'success' | 'failed' | 'timeout' -} - -export type hub2WebhookContext = deviceWebhookContext & { - temperature: number - humidity: number - lightLevel: number - scale: 'CELSIUS' | 'FAHRENHEIT' -} - -export type batteryCirculatorFanWebhookContext = deviceWebhookContext & { - mode: 'direct' | 'natural' | 'sleep' | 'baby' - version: string - battery: number - powerState: 'ON' | 'OFF' - nightStatus: 'off' | 1 | 2 - oscillation: 'on' | 'off' - verticalOscillation: 'on' | 'off' - chargingStatus: 'charging' | 'uncharged' - fanSpeed: number // 1~100 -} - -export type blindTiltWebhookContext = deviceWebhookContext & { - version: string - calibrate: boolean - group: boolean - direction: string - slidePosition: number // 0~100 - battery: number -} - -export type humidifierWebhookContext = deviceWebhookContext & { - temperature: number - humidity: number - scale: 'CELSIUS' | 'FAHRENHEIT' -} diff --git a/src/types/irdevicelist.ts b/src/types/irdevicelist.ts deleted file mode 100644 index a2a67bfe..00000000 --- a/src/types/irdevicelist.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* Copyright(C) 2017-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. - * - * irdevicelist.ts: @switchbot/homebridge-switchbot platform class. - */ -// a list of virtual infrared remote devices that are linked to SwitchBot Hubs. -export interface infraredRemoteList { - device: irdevice[] -} - -export interface irdevice { - deviceId?: string - deviceName: string - remoteType: string - hubDeviceId: string -} diff --git a/src/types/pushbody.ts b/src/types/pushbody.ts deleted file mode 100644 index 9e3ed39d..00000000 --- a/src/types/pushbody.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* Copyright(C) 2017-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved. - * - * pushbody.ts: @switchbot/homebridge-switchbot platform class. - */ -export interface body { - command: string - parameter: string - commandType: string -} diff --git a/src/utils.ts b/src/utils.ts index 05d98ff7..91dd3746 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,113 +2,9 @@ * * util.ts: @switchbot/homebridge-switchbot platform class. */ -import type { devicesConfig } from './settings.js' -import type { blindTilt, curtain, curtain3, device } from './types/devicelist.js' - -export enum SwitchBotModel { - HubMini = 'W0202200', - HubPlus = 'SwitchBot Hub S1', - Hub2 = 'W3202100', - Bot = 'SwitchBot S1', - Curtain = 'W0701600', - Curtain3 = 'W2400000', - Humidifier = 'W0801800', - Plug = 'SP11', // Currently only available in Japan - Meter = 'SwitchBot MeterTH S1', - MeterPlusJP = 'W2201500', - MeterPlusUS = 'W2301500', - OutdoorMeter = 'W3400010', - MotionSensor = 'W1101500', - ContactSensor = 'W1201500', - ColorBulb = 'W1401400', - StripLight = 'W1701100', - PlugMiniUS = 'W1901400/W1901401', - PlugMiniJP = 'W2001400/W2001401', - Lock = 'W1601700', - LockPro = 'W3500000', - Keypad = 'W2500010', - KeypadTouch = 'W2500020', - K10 = 'K10+', - WoSweeper = 'WoSweeper', - WoSweeperMini = 'WoSweeperMini', - RobotVacuumCleanerS1 = 'W3011000', // Currently only available in Japan. - RobotVacuumCleanerS1Plus = 'W3011010', // Currently only available in Japan. - RobotVacuumCleanerS10 = 'W3211800', - Remote = 'Remote', - UniversalRemote = 'UniversalRemote', - CeilingLight = 'W2612230/W2612240', // Currently only available in Japan. - CeilingLightPro = 'W2612210/W2612220', // Currently only available in Japan. - IndoorCam = 'W1301200', - PanTiltCam = 'W1801200', - PanTiltCam2K = 'W3101100', - BlindTilt = 'W2701600', - BatteryCirculatorFan = 'W3800510', - WaterDetector = 'W4402000', - Unknown = 'Unknown', -} - -export enum SwitchBotBLEModel { - Bot = 'H', - Curtain = 'c', - Curtain3 = '{', - Humidifier = 'e', - Meter = 'T', - MeterPlus = 'i', - Hub2 = 'v', - OutdoorMeter = 'w', - MotionSensor = 's', - ContactSensor = 'd', - ColorBulb = 'u', - StripLight = 'r', - PlugMiniUS = 'g', - PlugMiniJP = 'j', - Lock = 'o', - CeilingLight = 'q', // Currently only available in Japan. - CeilingLightPro = 'n', // Currently only available in Japan. - BlindTilt = 'x', - Unknown = 'Unknown', -} +import type { blindTilt, curtain, curtain3, device } from 'node-switchbot' -export enum SwitchBotBLEModelName { - Bot = 'WoHand', - Hub2 = 'WoHub2', - ColorBulb = 'WoBulb', - Curtain = 'WoCurtain', - Curtain3 = 'WoCurtain3', - Humidifier = 'WoHumi', - Meter = 'WoSensorTH', - Lock = 'WoSmartLock', - PlugMini = 'WoPlugMini', - StripLight = 'WoStrip', - MeterPlus = 'WoSensorTHPlus', - OutdoorMeter = 'WoIOSensorTH', - ContactSensor = 'WoContact', - MotionSensor = 'WoMotion', - BlindTilt = 'WoBlindTilt', - Unknown = 'Unknown', -} - -export enum SwitchBotBLEModelFriendlyName { - Bot = 'Bot', - Hub2 = 'Hub 2', - ColorBulb = 'Color Bulb', - Curtain = 'Curtain', - Curtain3 = 'Curtain 3', - Humidifier = 'Humidifier', - Meter = 'Meter', - Lock = 'Lock', - LockPro = 'Lock Pro', - PlugMini = 'Plug Mini', - StripLight = 'Strip Light', - MeterPlus = 'Meter Plus', - OutdoorMeter = 'Outdoor Meter', - ContactSensor = 'Contact Sensor', - MotionSensor = 'Motion Sensor', - BlindTilt = 'Blind Tilt', - CeilingLight = 'Ceiling Light', - CeilingLightPro = 'Ceiling Light Pro', - Unknown = 'Unknown', -} +import type { devicesConfig } from './settings.js' export enum BlindTiltMappingMode { OnlyUp = 'only_up',