diff --git a/.vscode/settings.json b/.vscode/settings.json
index 57325d61..848d5c50 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -30,5 +30,9 @@
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
- "codeQL.githubDatabase.update": "never"
+ "codeQL.githubDatabase.update": "never",
+ "codeQL.githubDatabase.download": "never",
+ "[html]": {
+ "editor.defaultFormatter": "vscode.html-language-features"
+ }
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8872aca2..3fde7aa4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,19 @@
All notable changes to this project will be documented in this file. This project uses [Semantic Versioning](https://semver.org/)
+## [3.5.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v3.5.0) (2024-05-26)
+
+### What's Changed
+- Add Support for `Water Detector`
+- Add Support for `Battery Circulator Fan`
+- Add BLE support for `Smart Lock`
+- Add `K10+` deviceType Support
+- Add Support for `maxRetries` and `delayBetweenRetries` on OpenAPI status refreshes based on [#959](https://github.com/OpenWonderLabs/homebridge-switchbot/issues/959#issuecomment-2094879876), Thanks [@sametguzeldev](https://github.com/sametguzeldev)
+- Major Refactoring of `device` and `irdevice` files.
+- Housekeeping and updated dependencies.
+
+**Full Changelog**: https://github.com/OpenWonderLabs/homebridge-switchbot/compare/v3.4.0...v3.5.0
+
## [3.4.0](https://github.com/OpenWonderLabs/homebridge-switchbot/releases/tag/v3.4.0) (2024-02-11)
### What's Changed
diff --git a/config.schema.json b/config.schema.json
index e3643ad8..08acf3b4 100644
--- a/config.schema.json
+++ b/config.schema.json
@@ -149,6 +149,12 @@
"WoIOSensor"
]
},
+ {
+ "title": "Water Detector",
+ "enum": [
+ "Water Detector"
+ ]
+ },
{
"title": "Meter",
"enum": [
@@ -302,9 +308,30 @@
"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);"
+ "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",
@@ -472,14 +499,87 @@
"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].configDeviceType === 'WoIOSensor') && model.options.devices[arrayIndices].deviceId);"
+ "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].configDeviceType === 'MeterPlus' || model.options.devices[arrayIndices].configDeviceType === 'Meter Plus (JP)') && model.options.devices[arrayIndices].deviceId);"
}
},
+ "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"
+ ]
+ }
+ ]
+ },
"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].configDeviceType === 'WoIOSensor') && model.options.devices[arrayIndices].deviceId);"
+ "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].configDeviceType === 'Meter' || model.options.devices[arrayIndices].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);"
+ }
+ },
+ "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"
+ ]
+ }
+ ]
+ },
+ "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);"
}
}
}
@@ -821,14 +921,14 @@
"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);"
+ "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);"
}
},
"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);"
+ "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);"
}
}
}
@@ -840,6 +940,22 @@
"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'));"
}
},
+ "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'));"
+ }
+ },
"maxRetry": {
"title": "Max Retries for BLE",
"type": "number",
@@ -853,28 +969,28 @@
"type": "string",
"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 === '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 && (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)'));"
}
},
"mqttOptions": {
"title": "MQTT Options",
"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 === '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 && 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)'));"
}
},
"mqttPubOptions": {
"title": "MQTT Pub Options",
"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 === '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 && 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",
"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 === '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 && (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)'));"
}
},
"firmware": {
@@ -882,14 +998,29 @@
"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);"
+ "functionBody": "return (model.options && model.options.devices && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].deviceId && model.options.devices[arrayIndices].configDeviceType);"
}
},
"refreshRate": {
"title": "Device Refresh Rate",
"type": "number",
"placeholder": 360,
- "description": "Indicates the number of seconds between polls of SwitchBot API.",
+ "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": "Device Update 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);"
+ }
+ },
+ "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);"
}
@@ -898,7 +1029,7 @@
"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);"
+ "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].deviceId && !model.options.devices[arrayIndices].hide_device && model.options.devices[arrayIndices].configDeviceType);"
}
},
"external": {
@@ -1521,17 +1652,30 @@
"type": "string",
"placeholder": "http://${FQDN}:${PORT}/${PATH}"
},
+ "maxRetries": {
+ "title": "Max Retries for OpenAPI",
+ "type": "number",
+ "placeholder": 5
+ },
+ "delayBetweenRetries": {
+ "title": "Delay Between Retries for OpenAPI (In Seconds)",
+ "type": "number",
+ "placeholder": 3
+ },
"refreshRate": {
- "title": "Refresh Rate",
+ "title": "Device Refresh Rate",
"type": "number",
- "placeholder": 360,
- "description": "Indicates the number of seconds between polls of SwitchBot API."
+ "placeholder": 360
+ },
+ "updateRate": {
+ "title": "Device Update Rate",
+ "type": "number",
+ "placeholder": 5
},
"pushRate": {
- "title": "Push Rate",
+ "title": "Device Push Rate",
"type": "number",
- "placeholder": 1,
- "description": "Indicates the number of seconds between pushes to SwitchBot API."
+ "placeholder": 1
},
"logging": {
"title": "Logging Setting",
@@ -1597,7 +1741,8 @@
"title": "{{ value.configDeviceName || value.deviceId || 'New SwitchBot Device' }}",
"expandable": true,
"expanded": false,
- "orderable": false,
+ "draggable": true,
+ "orderable": true,
"items": [
"options.devices[].configDeviceName",
"options.devices[].deviceId",
@@ -1606,10 +1751,13 @@
"options.devices[].connectionType",
"options.devices[].webhook",
"options.devices[].hub.hide_temperature",
+ "options.devices[].hub.convertUnitTo",
"options.devices[].hub.hide_humidity",
"options.devices[].hub.hide_lightsensor",
"options.devices[].scanDuration",
"options.devices[].disableCaching",
+ "options.devices[].maxRetries",
+ "options.devices[].delayBetweenRetries",
"options.devices[].maxRetry",
"options.devices[].bot.mode",
"options.devices[].bot.deviceType",
@@ -1617,7 +1765,12 @@
"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[].waterdetector.hide_leak",
"options.devices[].humidifier.set_minStep",
"options.devices[].humidifier.hide_temperature",
"options.devices[].curtain.set_minStep",
@@ -1654,7 +1807,18 @@
"options.devices[].mqttPubOptions",
"options.devices[].history",
"options.devices[].firmware",
- "options.devices[].refreshRate",
+ {
+ "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"
@@ -1675,7 +1839,8 @@
"title": "{{ value.configDeviceName || value.deviceId || 'New IR Device' }}",
"expandable": true,
"expanded": false,
- "orderable": false,
+ "draggable": true,
+ "orderable": true,
"items": [
"options.irdevices[].configDeviceName",
"options.irdevices[].deviceId",
@@ -1717,22 +1882,20 @@
"expanded": false,
"items": [
"options.webhookURL",
- {
- "type": "help",
- "helpvalue": "
Refresh Rate
Refresh Rate indicates the number of seconds between polls of SwitchBot API."
- },
+ "options.maxRetries",
+ "options.delayBetweenRetries",
{
"key": "options.refreshRate",
- "notitle": true
- },
+ "description": "Specifies the interval, in seconds, for retrieving the latest device status from the SwitchBot API."
+ },
{
- "type": "help",
- "helpvalue": "Push Rate
Push Rate indicates the number of seconds between pushes to SwitchBot API, Currently only for Curtains."
- },
+ "key": "options.updateRate",
+ "description": "Specifies the interval, in seconds, at which devices will request updates from the SwitchBot API while in motion, for Curtain(s) and Blind Tilt(s) only."
+ },
{
"key": "options.pushRate",
- "notitle": true
- },
+ "description": "Specifies the interval, in seconds, between pushes to the SwitchBot API."
+ },
"options.logging"
]
}
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 00000000..6890c8c0
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,96 @@
+import pluginJs from '@eslint/js';
+import tseslint from 'typescript-eslint';
+import stylistic from '@stylistic/eslint-plugin'
+
+
+export default tseslint.config({
+ plugins: {
+ '@stylistic': stylistic,
+ '@typescript-eslint': tseslint.plugin,
+ },
+ languageOptions: {
+ parser: tseslint.parser,
+ parserOptions: {
+ project: true,
+ },
+ },
+ files: ['**/*.ts'],
+ ignores: ['.dist/*'],
+ extends: [
+ pluginJs.configs.recommended,
+ ...tseslint.configs.recommended,
+ ],
+ rules: {
+ '@typescript-eslint/array-type': 'error',
+ '@typescript-eslint/consistent-type-imports': 'error',
+ '@stylistic/type-annotation-spacing': 'error',
+ '@stylistic/quotes': [
+ 'warn',
+ 'single',
+ ],
+ '@stylistic/indent': [
+ 'warn',
+ 2,
+ {
+ 'SwitchCase': 1,
+ },
+ ],
+ '@stylistic/linebreak-style': [
+ 'warn',
+ 'unix',
+ ],
+ '@stylistic/semi': [
+ 'warn',
+ 'always',
+ ],
+ '@stylistic/comma-dangle': [
+ 'warn',
+ 'always-multiline',
+ ],
+ '@stylistic/dot-notation': 'off',
+ 'eqeqeq': 'warn',
+ 'curly': [
+ 'warn',
+ 'all',
+ ],
+ '@stylistic/brace-style': [
+ 'warn',
+ ],
+ 'prefer-arrow-callback': [
+ 'warn',
+ ],
+ '@stylistic/max-len': [
+ 'warn',
+ 150,
+ ],
+ 'no-console': [
+ 'warn',
+ ], // use the provided Homebridge log method instead
+ 'no-non-null-assertion': [
+ 'off',
+ ],
+ '@stylistic/comma-spacing': [
+ 'error',
+ ],
+ '@stylistic/no-multi-spaces': [
+ 'warn',
+ {
+ 'ignoreEOLComments': true,
+ },
+ ],
+ '@stylistic/no-trailing-spaces': [
+ 'warn',
+ ],
+ '@stylistic/lines-between-class-members': [
+ 'warn',
+ 'always',
+ {
+ 'exceptAfterSingleLine': true,
+ },
+ ],
+ '@typescript-eslint/explicit-function-return-type': 'off',
+ '@typescript-eslint/no-non-null-assertion': 'off',
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
+ '@typescript-eslint/no-explicit-any': 'off',
+ },
+});
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 376c65dc..272fbd64 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@switchbot/homebridge-switchbot",
- "version": "3.4.0",
+ "version": "3.5.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@switchbot/homebridge-switchbot",
- "version": "3.4.0",
+ "version": "3.5.0",
"funding": [
{
"type": "Paypal",
@@ -19,47 +19,41 @@
],
"license": "ISC",
"dependencies": {
- "@homebridge/plugin-ui-utils": "^1.0.1",
+ "@homebridge/plugin-ui-utils": "^1.0.3",
"async-mqtt": "^2.6.3",
"fakegato-history": "^0.6.4",
- "homebridge-lib": "^6.7.3",
+ "homebridge-lib": "^7.0.1",
"rxjs": "^7.8.1",
- "undici": "^6.6.2"
+ "undici": "^6.18.1"
},
"devDependencies": {
- "@types/node": "^20.11.17",
- "@typescript-eslint/eslint-plugin": "^6.21.0",
- "@typescript-eslint/parser": "^6.21.0",
- "eslint": "^8.56.0",
- "homebridge": "^1.7.0",
- "homebridge-config-ui-x": "4.55.1",
- "nodemon": "^3.0.3",
- "npm-check-updates": "^16.14.15",
- "rimraf": "^5.0.5",
+ "@eslint/js": "^9.3.0",
+ "@stylistic/eslint-plugin": "^2.1.0",
+ "@types/eslint__js": "^8.42.3",
+ "@types/node": "^20.12.12",
+ "eslint": "^9.3.0",
+ "globals": "^15.3.0",
+ "homebridge": "^1.8.2",
+ "homebridge-config-ui-x": "4.56.2",
+ "nodemon": "^3.1.1",
+ "npm-check-updates": "^16.14.20",
+ "rimraf": "^5.0.7",
"ts-node": "^10.9.2",
- "typescript": "^5.3.3"
+ "typescript": "^5.4.5",
+ "typescript-eslint": "^8.0.0-alpha.14"
},
"engines": {
- "homebridge": "^1.7.0",
- "node": "^18 || ^20"
+ "homebridge": "^1.8.2",
+ "node": "^18 || ^20 || ^22"
},
"optionalDependencies": {
- "node-switchbot": "2.0.3"
- }
- },
- "node_modules/@aashutoshrathi/word-wrap": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
- "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
+ "node-switchbot": "2.1.1"
}
},
"node_modules/@abandonware/bluetooth-hci-socket": {
- "version": "0.5.3-11",
- "resolved": "https://registry.npmjs.org/@abandonware/bluetooth-hci-socket/-/bluetooth-hci-socket-0.5.3-11.tgz",
- "integrity": "sha512-gOFnpLtSzYFSsNgR609TdFUGVvX7dhc8nrarcfmWZNgtlzRS0IJq7/xxpTw/G4SicEkNlT6lDi6eHwurIT+axg==",
+ "version": "0.5.3-12",
+ "resolved": "https://registry.npmjs.org/@abandonware/bluetooth-hci-socket/-/bluetooth-hci-socket-0.5.3-12.tgz",
+ "integrity": "sha512-qo2cBoh94j6RPusaNXSLYI8Bzxuz01Bx3MD80a/QYzhHED/FZ6Y0k2w2kRbfIA2EEhFSCbXrBZDQlpilL4nbxA==",
"hasInstallScript": true,
"optional": true,
"os": [
@@ -82,9 +76,9 @@
}
},
"node_modules/@abandonware/noble": {
- "version": "1.9.2-24",
- "resolved": "https://registry.npmjs.org/@abandonware/noble/-/noble-1.9.2-24.tgz",
- "integrity": "sha512-9kP3DWP1IxuG5PYjz4rqGXl3u6JIu654iibR6Teasgk5UsdH9nkTi2teYuUl54yoOz6vjgXNjofwXp3naJvhfQ==",
+ "version": "1.9.2-25",
+ "resolved": "https://registry.npmjs.org/@abandonware/noble/-/noble-1.9.2-25.tgz",
+ "integrity": "sha512-FBgwjWkXF3VLb0V0hdOeeVhuupdfuagkBz9E0Xjfmr8MYuR5JjnXHty0/6Q38d4v//EhX7oZaZXBiZWrhWKKbg==",
"hasInstallScript": true,
"optional": true,
"os": [
@@ -106,19 +100,6 @@
"@abandonware/bluetooth-hci-socket": "^0.5.3-11"
}
},
- "node_modules/@babel/runtime-corejs3": {
- "version": "7.23.9",
- "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.23.9.tgz",
- "integrity": "sha512-oeOFTrYWdWXCvXGB5orvMTJ6gCZ9I6FBjR+M38iKNXCsPxr4xT0RTdg5uz1H7QP8pp74IzPtwritEr+JscqHXQ==",
- "dev": true,
- "dependencies": {
- "core-js-pure": "^3.30.2",
- "regenerator-runtime": "^0.14.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@colors/colors": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
@@ -156,6 +137,18 @@
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
}
},
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
"node_modules/@eslint-community/regexpp": {
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
@@ -166,15 +159,15 @@
}
},
"node_modules/@eslint/eslintrc": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
- "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz",
+ "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==",
"dev": true,
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
- "espree": "^9.6.0",
- "globals": "^13.19.0",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
"js-yaml": "^4.1.0",
@@ -182,41 +175,31 @@
"strip-json-comments": "^3.1.1"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
"engines": {
- "node": "*"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@eslint/js": {
- "version": "8.56.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz",
- "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==",
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.3.0.tgz",
+ "integrity": "sha512-niBqk8iwv96+yuTwjM6bWg8ovzAPF9qkICsGtcoa5/dmqcEMfdwNAX7+/OHcJHc7wj7XqPxH98oAHytFYlw6Sw==",
"dev": true,
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@fastify/accept-negotiator": {
@@ -240,15 +223,15 @@
}
},
"node_modules/@fastify/ajv-compiler/node_modules/ajv": {
- "version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+ "version": "8.14.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz",
+ "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==",
"dev": true,
"dependencies": {
- "fast-deep-equal": "^3.1.1",
+ "fast-deep-equal": "^3.1.3",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
- "uri-js": "^4.2.2"
+ "uri-js": "^4.4.1"
},
"funding": {
"type": "github",
@@ -262,25 +245,22 @@
"dev": true
},
"node_modules/@fastify/busboy": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-1.2.1.tgz",
- "integrity": "sha512-7PQA7EH43S0CxcOa9OeAnaeA0oQ+e/DHNPZwSQM9CQHW76jle5+OvLdibRp/Aafs9KXbLhxyjOTkRjWUbQEd3Q==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
+ "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
"dev": true,
- "dependencies": {
- "text-decoding": "^1.0.0"
- },
"engines": {
"node": ">=14"
}
},
"node_modules/@fastify/cors": {
- "version": "8.4.2",
- "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-8.4.2.tgz",
- "integrity": "sha512-IVynbcPG9eWiJ0P/A1B+KynmiU/yTYbu3ooBUSIeHfca/N1XLb9nIJVCws+YTr2q63MA8Y6QLeXQczEv4npM9g==",
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-9.0.1.tgz",
+ "integrity": "sha512-YY9Ho3ovI+QHIL2hW+9X4XqQjXLjJqsU+sMV/xFsxZkE8p3GNnYVFpoOxF7SsP5ZL76gwvbo3V9L+FIekBGU4Q==",
"dev": true,
"dependencies": {
"fastify-plugin": "^4.0.0",
- "mnemonist": "0.39.5"
+ "mnemonist": "0.39.6"
}
},
"node_modules/@fastify/deepmerge": {
@@ -346,22 +326,20 @@
}
},
"node_modules/@fastify/middie/node_modules/path-to-regexp": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz",
- "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==",
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz",
+ "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==",
"dev": true
},
"node_modules/@fastify/multipart": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-8.0.0.tgz",
- "integrity": "sha512-xaH1pGIqYnIJjYs5qG6ryhPSFnWuJIfSXYqEUtzmcyREkMk0SwONd2y+SZ9JXfDmETAC/Ogtc/SRbz+AjZhCkw==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-8.2.0.tgz",
+ "integrity": "sha512-OZ8nsyyoS2TV7Yeu3ZdrdDGsKUTAbfjrKC9jSxGgT2qdgek+BxpWX31ZubTrWMNZyU5xwk4ox6AvTjAbYWjrWg==",
"dev": true,
"dependencies": {
- "@fastify/busboy": "^1.0.0",
+ "@fastify/busboy": "^2.1.0",
"@fastify/deepmerge": "^1.0.0",
"@fastify/error": "^3.0.0",
- "@fastify/swagger": "^8.3.1",
- "@fastify/swagger-ui": "^1.8.0",
"fastify-plugin": "^4.0.0",
"secure-json-parse": "^2.4.0",
"stream-wormhole": "^1.1.0"
@@ -381,43 +359,17 @@
}
},
"node_modules/@fastify/static": {
- "version": "6.12.0",
- "resolved": "https://registry.npmjs.org/@fastify/static/-/static-6.12.0.tgz",
- "integrity": "sha512-KK1B84E6QD/FcQWxDI2aiUCwHxMJBI1KeCUzm1BwYpPY1b742+jeKruGHP2uOluuM6OkBPI8CIANrXcCRtC2oQ==",
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@fastify/static/-/static-7.0.2.tgz",
+ "integrity": "sha512-5opbHpZj29EGVBNgELW6gDkueiFWxjLsLVQQCgKencJctq0aqk3vBlkO97z5It4zaSAb3FXOeAxm7KP2tL/hQA==",
"dev": true,
"dependencies": {
"@fastify/accept-negotiator": "^1.0.0",
"@fastify/send": "^2.0.0",
"content-disposition": "^0.5.3",
"fastify-plugin": "^4.0.0",
- "glob": "^8.0.1",
- "p-limit": "^3.1.0"
- }
- },
- "node_modules/@fastify/swagger": {
- "version": "8.14.0",
- "resolved": "https://registry.npmjs.org/@fastify/swagger/-/swagger-8.14.0.tgz",
- "integrity": "sha512-sGiznEb3rl6pKGGUZ+JmfI7ct5cwbTQGo+IjewaTvtzfrshnryu4dZwEsjw0YHABpBA+kCz3kpRaHB7qpa67jg==",
- "dev": true,
- "dependencies": {
- "fastify-plugin": "^4.0.0",
- "json-schema-resolver": "^2.0.0",
- "openapi-types": "^12.0.0",
- "rfdc": "^1.3.0",
- "yaml": "^2.2.2"
- }
- },
- "node_modules/@fastify/swagger-ui": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/@fastify/swagger-ui/-/swagger-ui-1.10.2.tgz",
- "integrity": "sha512-f2mRqtblm6eRAFQ3e8zSngxVNEtiYY7rISKQVjPA++ZsWc5WYlPVTb6Bx0G/zy0BIoucNqDr/Q2Vb/kTYkOq1A==",
- "dev": true,
- "dependencies": {
- "@fastify/static": "^6.0.0",
- "fastify-plugin": "^4.0.0",
- "openapi-types": "^12.0.2",
- "rfdc": "^1.3.0",
- "yaml": "^2.2.2"
+ "fastq": "^1.17.0",
+ "glob": "^10.3.4"
}
},
"node_modules/@gar/promisify": {
@@ -427,9 +379,9 @@
"dev": true
},
"node_modules/@homebridge/ciao": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.1.8.tgz",
- "integrity": "sha512-Atn8+vwYtfI/J6nYCOVm4uVBAmiQO4rPi0umVbh766cf/OsVxQ+Qedbo9lxIf15iDsMbBlDV7T1wATdHqI5lXw==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.2.0.tgz",
+ "integrity": "sha512-2Qa8MVC7Q5DKH6iXh6cRvqz9VJYVpVZ+whHKrnr8YdPkXxc67kiQ9IOxMb0ydokDTETBVyXgr1m+HrheBtqDoQ==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
@@ -441,27 +393,40 @@
"ciao-bcs": "lib/bonjour-conformance-testing.js"
},
"engines": {
- "node": ">=14"
+ "node": "^18 || ^20"
}
},
"node_modules/@homebridge/dbus-native": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/@homebridge/dbus-native/-/dbus-native-0.5.1.tgz",
- "integrity": "sha512-7xXz3R1W/kcbfQOGp32y4K7etqtowICR1vpx8j85KwPYXbNQrgiZ3zcwDYgDGBWq3FD9xzsW7h4YWJ4vTR2seQ==",
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@homebridge/dbus-native/-/dbus-native-0.6.0.tgz",
+ "integrity": "sha512-xObqQeYHTXmt6wsfj10+krTo4xbzR9BgUfX2aQ+edDC9nc4ojfzLScfXCh3zluAm6UCowKw+AFfXn6WLWUOPkg==",
"dev": true,
"dependencies": {
"@homebridge/long": "^5.2.1",
- "@homebridge/put": "~0.0.8",
- "event-stream": "^4.0.0",
- "hexy": "^0.2.10",
+ "@homebridge/put": "^0.0.8",
+ "event-stream": "^4.0.1",
+ "hexy": "^0.3.5",
"minimist": "^1.2.6",
- "safe-buffer": "^5.1.1",
- "xml2js": "^0.5.0"
+ "safe-buffer": "^5.1.2",
+ "xml2js": "^0.6.2"
},
"bin": {
"dbus2js": "bin/dbus2js.js"
}
},
+ "node_modules/@homebridge/hap-client": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@homebridge/hap-client/-/hap-client-1.10.0.tgz",
+ "integrity": "sha512-jbCfsrT97EF9IkW6Wj9fq0ALeSa1y+3UtZ2V+QsacHBd4cfrBr8S5UvlgU+zemctb/EllxMA8S92XqWg5yL2UA==",
+ "dev": true,
+ "dependencies": {
+ "axios": "^1.6.8",
+ "bonjour-service": "^1.2.1",
+ "decamelize": "^5.0.1",
+ "inflection": "^3.0.0",
+ "source-map-support": "^0.5.21"
+ }
+ },
"node_modules/@homebridge/long": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/@homebridge/long/-/long-5.2.1.tgz",
@@ -480,9 +445,9 @@
}
},
"node_modules/@homebridge/plugin-ui-utils": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@homebridge/plugin-ui-utils/-/plugin-ui-utils-1.0.1.tgz",
- "integrity": "sha512-Qxpu+HTb5F3tz6iV+gls/snzKPcP/9lOHwoV8IpJlXOeVWj3QMeMuw19dHH8ggLPMm4GaKuObrWjU9yOMENKkw=="
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@homebridge/plugin-ui-utils/-/plugin-ui-utils-1.0.3.tgz",
+ "integrity": "sha512-p2S/czGYNRnRtMICxBUk4Uar+KCezfyxjqfStfxKgykD2082SNayVDncYUK1xRai78EGHCbif9eoyrmDweh4tQ=="
},
"node_modules/@homebridge/put": {
"version": "0.0.8",
@@ -494,12 +459,12 @@
}
},
"node_modules/@humanwhocodes/config-array": {
- "version": "0.11.14",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
- "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
+ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
"dev": true,
"dependencies": {
- "@humanwhocodes/object-schema": "^2.0.2",
+ "@humanwhocodes/object-schema": "^2.0.3",
"debug": "^4.3.1",
"minimatch": "^3.0.5"
},
@@ -507,28 +472,6 @@
"node": ">=10.10.0"
}
},
- "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@humanwhocodes/config-array/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/@humanwhocodes/module-importer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
@@ -543,11 +486,24 @@
}
},
"node_modules/@humanwhocodes/object-schema": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
- "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
"dev": true
},
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz",
+ "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -577,29 +533,6 @@
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
- "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
- "version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
- "devOptional": true
- },
- "node_modules/@isaacs/cliui/node_modules/string-width": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
- "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
- "devOptional": true,
- "dependencies": {
- "eastasianwidth": "^0.2.0",
- "emoji-regex": "^9.2.2",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/@isaacs/cliui/node_modules/strip-ansi": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
@@ -616,9 +549,9 @@
}
},
"node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
- "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"engines": {
"node": ">=6.0.0"
@@ -641,9 +574,9 @@
}
},
"node_modules/@leichtgewicht/ip-codec": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
- "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A=="
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
+ "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw=="
},
"node_modules/@lukeed/csprng": {
"version": "1.1.0",
@@ -695,20 +628,11 @@
"node": ">= 6.0.0"
}
},
- "node_modules/@mapbox/node-pre-gyp/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "optional": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/@mapbox/node-pre-gyp/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"optional": true,
"dependencies": {
"fs.realpath": "^1.0.0",
@@ -738,22 +662,11 @@
"node": ">= 6"
}
},
- "node_modules/@mapbox/node-pre-gyp/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "optional": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/@mapbox/node-pre-gyp/node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
"optional": true,
"dependencies": {
"glob": "^7.1.3"
@@ -765,22 +678,27 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/@microsoft/tsdoc": {
+ "version": "0.14.2",
+ "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz",
+ "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==",
+ "dev": true
+ },
"node_modules/@nestjs/axios": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.0.1.tgz",
- "integrity": "sha512-VlOZhAGDmOoFdsmewn8AyClAdGpKXQQaY1+3PGB+g6ceurGIdTxZgRX3VXc1T6Zs60PedWjg3A82TDOB05mrzQ==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.0.2.tgz",
+ "integrity": "sha512-Z6GuOUdNQjP7FX+OuV2Ybyamse+/e0BFdTWBX5JxpBDKA+YkdLynDgG6HTF04zy6e9zPa19UX0WA2VDoehwhXQ==",
"dev": true,
"peerDependencies": {
"@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0",
"axios": "^1.3.1",
- "reflect-metadata": "^0.1.12",
"rxjs": "^6.0.0 || ^7.0.0"
}
},
"node_modules/@nestjs/common": {
- "version": "10.3.0",
- "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.0.tgz",
- "integrity": "sha512-DGv34UHsZBxCM3H5QGE2XE/+oLJzz5+714JQjBhjD9VccFlQs3LRxo/epso4l7nJIiNlZkPyIUC8WzfU/5RTsQ==",
+ "version": "10.3.7",
+ "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.3.7.tgz",
+ "integrity": "sha512-gKFtFzcJznrwsRYjtNZoPAvSOPYdNgxbTYoAyLTpoy393cIKgLmJTHu6ReH8/qIB9AaZLdGaFLkx98W/tFWFUw==",
"dev": true,
"dependencies": {
"iterare": "1.2.1",
@@ -794,7 +712,7 @@
"peerDependencies": {
"class-transformer": "*",
"class-validator": "*",
- "reflect-metadata": "^0.1.12",
+ "reflect-metadata": "^0.1.12 || ^0.2.0",
"rxjs": "^7.1.0"
},
"peerDependenciesMeta": {
@@ -807,9 +725,9 @@
}
},
"node_modules/@nestjs/core": {
- "version": "10.3.0",
- "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.0.tgz",
- "integrity": "sha512-N06P5ncknW/Pm8bj964WvLIZn2gNhHliCBoAO1LeBvNImYkecqKcrmLbY49Fa1rmMfEM3MuBHeDys3edeuYAOA==",
+ "version": "10.3.7",
+ "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.3.7.tgz",
+ "integrity": "sha512-hsdlnfiQ3kgqHL5k7js3CU0PV7hBJVi+LfFMgCkoagRxNMf67z0GFGeOV2jk5d65ssB19qdYsDa1MGVuEaoUpg==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
@@ -829,7 +747,7 @@
"@nestjs/microservices": "^10.0.0",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/websockets": "^10.0.0",
- "reflect-metadata": "^0.1.12",
+ "reflect-metadata": "^0.1.12 || ^0.2.0",
"rxjs": "^7.1.0"
},
"peerDependenciesMeta": {
@@ -858,15 +776,15 @@
}
},
"node_modules/@nestjs/mapped-types": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.4.tgz",
- "integrity": "sha512-xl+gUSp0B+ln1VSNoUftlglk8dfpUes3DHGxKZ5knuBxS5g2H/8p9/DSBOYWUfO5f4u9s6ffBPZ71WO+tbe5SA==",
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz",
+ "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==",
"dev": true,
"peerDependencies": {
"@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0",
"class-transformer": "^0.4.0 || ^0.5.0",
"class-validator": "^0.13.0 || ^0.14.0",
- "reflect-metadata": "^0.1.12"
+ "reflect-metadata": "^0.1.12 || ^0.2.0"
},
"peerDependenciesMeta": {
"class-transformer": {
@@ -888,16 +806,16 @@
}
},
"node_modules/@nestjs/platform-fastify": {
- "version": "10.3.0",
- "resolved": "https://registry.npmjs.org/@nestjs/platform-fastify/-/platform-fastify-10.3.0.tgz",
- "integrity": "sha512-ka4r/cPWM5y/dXoi9dj6pn1o3WLnfImy2bT3aYVasiDsJff2cd3h/ThugwxjdH0BHUpLSPnawEGzADAcO8Fqug==",
+ "version": "10.3.7",
+ "resolved": "https://registry.npmjs.org/@nestjs/platform-fastify/-/platform-fastify-10.3.7.tgz",
+ "integrity": "sha512-J7ICAOC/zTSfJLTWwvVLK9LON+Dw/NdgPQwPBsi3GNIw3TLLXLrQ4WXnHNER8gnXS3sfKOIngRLusMirLqYjEQ==",
"dev": true,
"dependencies": {
- "@fastify/cors": "8.4.2",
+ "@fastify/cors": "9.0.1",
"@fastify/formbody": "7.4.0",
"@fastify/middie": "8.3.0",
- "fastify": "4.25.1",
- "light-my-request": "5.11.0",
+ "fastify": "4.26.2",
+ "light-my-request": "5.12.0",
"path-to-regexp": "3.2.0",
"tslib": "2.6.2"
},
@@ -906,7 +824,7 @@
"url": "https://opencollective.com/nest"
},
"peerDependencies": {
- "@fastify/static": "^6.0.0",
+ "@fastify/static": "^6.0.0 || ^7.0.0",
"@fastify/view": "^7.0.0 || ^8.0.0",
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0"
@@ -921,12 +839,12 @@
}
},
"node_modules/@nestjs/platform-socket.io": {
- "version": "10.3.0",
- "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.3.0.tgz",
- "integrity": "sha512-Rdpk9OdsvJfsmVRtg4/0+cUdvOgBEb3F4zo2r4SBgxb0eaR3BHbhbXTJH/U7NvREIvvYbtSNoWI+h2taUEkXwg==",
+ "version": "10.3.7",
+ "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-10.3.7.tgz",
+ "integrity": "sha512-T9VbVgEUnbid/RiywN9/8YQ8pAGDP++0nX73l4kIWeDWkz5DEh4aLB7O/JvLA3/xRHdjTZ4RiRZazwqSWi1Sog==",
"dev": true,
"dependencies": {
- "socket.io": "4.7.2",
+ "socket.io": "4.7.5",
"tslib": "2.6.2"
},
"funding": {
@@ -940,24 +858,25 @@
}
},
"node_modules/@nestjs/swagger": {
- "version": "7.1.17",
- "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.1.17.tgz",
- "integrity": "sha512-ASCxBrvMEN2o/8vEEmrIPMNzrr/hVi7QIR4y1oNYvoBNXHuwoF1VSI3+4Rq/3xmwVnVveJxHlBIs2u5xY9VgGQ==",
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.3.1.tgz",
+ "integrity": "sha512-LUC4mr+5oAleEC/a2j8pNRh1S5xhKXJ1Gal5ZdRjt9XebQgbngXCdW7JTA9WOEcwGtFZN9EnKYdquzH971LZfw==",
"dev": true,
"dependencies": {
- "@nestjs/mapped-types": "2.0.4",
+ "@microsoft/tsdoc": "^0.14.2",
+ "@nestjs/mapped-types": "2.0.5",
"js-yaml": "4.1.0",
"lodash": "4.17.21",
"path-to-regexp": "3.2.0",
- "swagger-ui-dist": "5.10.3"
+ "swagger-ui-dist": "5.11.2"
},
"peerDependencies": {
- "@fastify/static": "^6.0.0",
+ "@fastify/static": "^6.0.0 || ^7.0.0",
"@nestjs/common": "^9.0.0 || ^10.0.0",
"@nestjs/core": "^9.0.0 || ^10.0.0",
"class-transformer": "*",
"class-validator": "*",
- "reflect-metadata": "^0.1.12"
+ "reflect-metadata": "^0.1.12 || ^0.2.0"
},
"peerDependenciesMeta": {
"@fastify/static": {
@@ -972,9 +891,9 @@
}
},
"node_modules/@nestjs/websockets": {
- "version": "10.3.0",
- "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.3.0.tgz",
- "integrity": "sha512-1cqh46s4iHLytExSWcvp/58pMZFAT7pQ59IAYQCSZz5xFq0lEGxd36C982KyROQIHfno8E+FWm71UhgVTwKsyA==",
+ "version": "10.3.7",
+ "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.3.7.tgz",
+ "integrity": "sha512-iYdsWiRNPUy0XzPoW44bx2MW1griuraTr5fNhoe2rUSNO0mEW1aeXp4v56KeZDLAss31WbeckC5P3N223Fys5g==",
"dev": true,
"dependencies": {
"iterare": "1.2.1",
@@ -985,7 +904,7 @@
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0",
"@nestjs/platform-socket.io": "^10.0.0",
- "reflect-metadata": "^0.1.12",
+ "reflect-metadata": "^0.1.12 || ^0.2.0",
"rxjs": "^7.1.0"
},
"peerDependenciesMeta": {
@@ -1030,34 +949,34 @@
}
},
"node_modules/@npmcli/agent": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.1.tgz",
- "integrity": "sha512-H4FrOVtNyWC8MUwL3UfjOsAihHvT1Pe8POj3JvjXhSTJipsZMtgUALCT4mGyYZNxymkUfOw3PUj6dE4QPp6osQ==",
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz",
+ "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==",
"optional": true,
"dependencies": {
"agent-base": "^7.1.0",
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.1",
"lru-cache": "^10.0.1",
- "socks-proxy-agent": "^8.0.1"
+ "socks-proxy-agent": "^8.0.3"
},
"engines": {
"node": "^16.14.0 || >=18.0.0"
}
},
"node_modules/@npmcli/agent/node_modules/lru-cache": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
- "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
+ "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
"optional": true,
"engines": {
"node": "14 || >=16.14"
}
},
"node_modules/@npmcli/fs": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz",
- "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz",
+ "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==",
"devOptional": true,
"dependencies": {
"semver": "^7.3.5"
@@ -1110,16 +1029,16 @@
}
},
"node_modules/@npmcli/installed-package-contents": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz",
- "integrity": "sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz",
+ "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==",
"dev": true,
"dependencies": {
"npm-bundled": "^3.0.0",
"npm-normalize-package-bin": "^3.0.0"
},
"bin": {
- "installed-package-contents": "lib/index.js"
+ "installed-package-contents": "bin/index.js"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
@@ -1139,20 +1058,11 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
- "node_modules/@npmcli/move-file/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/@npmcli/move-file/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
@@ -1169,18 +1079,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/@npmcli/move-file/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/@npmcli/move-file/node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
@@ -1197,6 +1095,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"dependencies": {
"glob": "^7.1.3"
@@ -1289,6 +1188,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz",
"integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==",
+ "deprecated": "This package is no longer supported.",
"dev": true,
"dependencies": {
"delegates": "^1.0.0",
@@ -1299,13 +1199,12 @@
}
},
"node_modules/@npmcli/run-script/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
+ "balanced-match": "^1.0.0"
}
},
"node_modules/@npmcli/run-script/node_modules/cacache": {
@@ -1337,19 +1236,11 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
- "node_modules/@npmcli/run-script/node_modules/cacache/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
"node_modules/@npmcli/run-script/node_modules/cacache/node_modules/glob": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
@@ -1377,6 +1268,12 @@
"node": ">=10"
}
},
+ "node_modules/@npmcli/run-script/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
"node_modules/@npmcli/run-script/node_modules/fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
@@ -1393,6 +1290,7 @@
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz",
"integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==",
+ "deprecated": "This package is no longer supported.",
"dev": true,
"dependencies": {
"aproba": "^1.0.3 || ^2.0.0",
@@ -1412,6 +1310,7 @@
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
@@ -1491,18 +1390,6 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
- "node_modules/@npmcli/run-script/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/@npmcli/run-script/node_modules/minipass": {
"version": "3.3.6",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
@@ -1615,6 +1502,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz",
"integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==",
+ "deprecated": "This package is no longer supported.",
"dev": true,
"dependencies": {
"are-we-there-yet": "^3.0.0",
@@ -1630,6 +1518,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"dependencies": {
"glob": "^7.1.3"
@@ -1641,6 +1530,12 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/@npmcli/run-script/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
"node_modules/@npmcli/run-script/node_modules/socks-proxy-agent": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz",
@@ -1667,6 +1562,20 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
+ "node_modules/@npmcli/run-script/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/@npmcli/run-script/node_modules/unique-filename": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz",
@@ -1771,33 +1680,10 @@
"@otplib/plugin-thirty-two": "^12.0.1"
}
},
- "node_modules/@oznu/hap-client": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/@oznu/hap-client/-/hap-client-1.9.0.tgz",
- "integrity": "sha512-dtx8SNLzT62VVwN2YwBVooLyKBrBphddVvYda8CqY5BHLzjK8SMDOF63t2nvp+MLwzWXKfpmcX1w+qS3V8J9Gw==",
- "dev": true,
- "dependencies": {
- "axios": "^0.27.2",
- "bonjour-service": "^1.0.12",
- "decamelize": "^3.2.0",
- "inflection": "^1.13.2",
- "source-map-support": "^0.5.19"
- }
- },
- "node_modules/@oznu/hap-client/node_modules/axios": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
- "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
- "dev": true,
- "dependencies": {
- "follow-redirects": "^1.14.9",
- "form-data": "^4.0.0"
- }
- },
- "node_modules/@pkgjs/parseargs": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
- "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
"dev": true,
"optional": true,
"engines": {
@@ -1915,28 +1801,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
- "node_modules/@sigstore/sign/node_modules/glob": {
- "version": "10.3.10",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
- "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
- "dev": true,
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^2.3.5",
- "minimatch": "^9.0.1",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
- "path-scurry": "^1.10.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/@sigstore/sign/node_modules/http-proxy-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
@@ -2072,11 +1936,362 @@
}
},
"node_modules/@socket.io/component-emitter": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz",
- "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
"dev": true
},
+ "node_modules/@stylistic/eslint-plugin": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.1.0.tgz",
+ "integrity": "sha512-cBBowKP2u/+uE5CzgH5w8pE9VKqcM7BXdIDPIbGt2rmLJGnA6MJPr9vYGaqgMoJFs7R/FzsMQerMvvEP40g2uw==",
+ "dev": true,
+ "dependencies": {
+ "@stylistic/eslint-plugin-js": "2.1.0",
+ "@stylistic/eslint-plugin-jsx": "2.1.0",
+ "@stylistic/eslint-plugin-plus": "2.1.0",
+ "@stylistic/eslint-plugin-ts": "2.1.0",
+ "@types/eslint": "^8.56.10"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-js": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.1.0.tgz",
+ "integrity": "sha512-gdXUjGNSsnY6nPyqxu6lmDTtVrwCOjun4x8PUn0x04d5ucLI74N3MT1Q0UhdcOR9No3bo5PGDyBgXK+KmD787A==",
+ "dev": true,
+ "dependencies": {
+ "@types/eslint": "^8.56.10",
+ "acorn": "^8.11.3",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.0.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-jsx": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-2.1.0.tgz",
+ "integrity": "sha512-mMD7S+IndZo2vxmwpHVTCwx2O1VdtE5tmpeNwgaEcXODzWV1WTWpnsc/PECQKIr/mkLPFWiSIqcuYNhQ/3l6AQ==",
+ "dev": true,
+ "dependencies": {
+ "@stylistic/eslint-plugin-js": "^2.1.0",
+ "@types/eslint": "^8.56.10",
+ "estraverse": "^5.3.0",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-plus": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-plus/-/eslint-plugin-plus-2.1.0.tgz",
+ "integrity": "sha512-S5QAlgYXESJaSBFhBSBLZy9o36gXrXQwWSt6QkO+F0SrT9vpV5JF/VKoh+ojO7tHzd8Ckmyouq02TT9Sv2B0zQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/eslint": "^8.56.10",
+ "@typescript-eslint/utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "eslint": "*"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/scope-manager": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz",
+ "integrity": "sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.10.0",
+ "@typescript-eslint/visitor-keys": "7.10.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/types": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.10.0.tgz",
+ "integrity": "sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz",
+ "integrity": "sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.10.0",
+ "@typescript-eslint/visitor-keys": "7.10.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/utils": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz",
+ "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "7.10.0",
+ "@typescript-eslint/types": "7.10.0",
+ "@typescript-eslint/typescript-estree": "7.10.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz",
+ "integrity": "sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.10.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-plus/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-plus/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-plus/node_modules/minimatch": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-ts": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-2.1.0.tgz",
+ "integrity": "sha512-2ioFibufHYBALx2TBrU4KXovCkN8qCqcb9yIHc0fyOfTaO5jw4d56WW7YRcF3Zgde6qFyXwAN6z/+w4pnmos1g==",
+ "dev": true,
+ "dependencies": {
+ "@stylistic/eslint-plugin-js": "2.1.0",
+ "@types/eslint": "^8.56.10",
+ "@typescript-eslint/utils": "^7.8.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/scope-manager": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz",
+ "integrity": "sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.10.0",
+ "@typescript-eslint/visitor-keys": "7.10.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/types": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.10.0.tgz",
+ "integrity": "sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz",
+ "integrity": "sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.10.0",
+ "@typescript-eslint/visitor-keys": "7.10.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/utils": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz",
+ "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "7.10.0",
+ "@typescript-eslint/types": "7.10.0",
+ "@typescript-eslint/typescript-estree": "7.10.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.56.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz",
+ "integrity": "sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/types": "7.10.0",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || >=20.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-ts/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-ts/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin-ts/node_modules/minimatch": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/@szmarczak/http-timer": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
@@ -2099,9 +2314,9 @@
}
},
"node_modules/@tsconfig/node10": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
- "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
+ "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
"dev": true
},
"node_modules/@tsconfig/node12": {
@@ -2144,6 +2359,30 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
+ "node_modules/@tufjs/models/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@tufjs/models/node_modules/minimatch": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/@types/cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
@@ -2159,6 +2398,31 @@
"@types/node": "*"
}
},
+ "node_modules/@types/eslint": {
+ "version": "8.56.10",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
+ "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/eslint__js": {
+ "version": "8.42.3",
+ "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz",
+ "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==",
+ "dev": true,
+ "dependencies": {
+ "@types/eslint": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "dev": true
+ },
"node_modules/@types/http-cache-semantics": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
@@ -2181,24 +2445,24 @@
}
},
"node_modules/@types/node": {
- "version": "20.11.17",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz",
- "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==",
+ "version": "20.12.12",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz",
+ "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
- "node_modules/@types/semver": {
- "version": "7.5.7",
- "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz",
- "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==",
+ "node_modules/@types/semver-utils": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@types/semver-utils/-/semver-utils-1.1.3.tgz",
+ "integrity": "sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==",
"dev": true
},
"node_modules/@types/validator": {
- "version": "13.11.9",
- "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.9.tgz",
- "integrity": "sha512-FCTsikRozryfayPuiI46QzH3fnrOoctTjvOYZkho9BTFLCOZ2rgZJHMOVgCOfttjPJcgOx52EpkY0CMfy87MIw==",
+ "version": "13.11.10",
+ "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.10.tgz",
+ "integrity": "sha512-e2PNXoXLr6Z+dbfx5zSh9TRlXJrELycxiaXznp4S5+D2M3b9bqJEitNHA5923jhnB2zzFiZHa2f0SI1HoIahpg==",
"dev": true
},
"node_modules/@types/w3c-web-usb": {
@@ -2208,33 +2472,31 @@
"optional": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
- "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==",
+ "version": "8.0.0-alpha.16",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0-alpha.16.tgz",
+ "integrity": "sha512-ZDVgR/z28jg3CPzQJqFIOQ/gshqf3NDw7zCu2jTeAYqtyXpCsAkAivvkeuuuXCypRl53cK16qDPlCguUCZW5Ow==",
"dev": true,
"dependencies": {
- "@eslint-community/regexpp": "^4.5.1",
- "@typescript-eslint/scope-manager": "6.21.0",
- "@typescript-eslint/type-utils": "6.21.0",
- "@typescript-eslint/utils": "6.21.0",
- "@typescript-eslint/visitor-keys": "6.21.0",
- "debug": "^4.3.4",
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.0.0-alpha.16",
+ "@typescript-eslint/type-utils": "8.0.0-alpha.16",
+ "@typescript-eslint/utils": "8.0.0-alpha.16",
+ "@typescript-eslint/visitor-keys": "8.0.0-alpha.16",
"graphemer": "^1.4.0",
- "ignore": "^5.2.4",
+ "ignore": "^5.3.1",
"natural-compare": "^1.4.0",
- "semver": "^7.5.4",
- "ts-api-utils": "^1.0.1"
+ "ts-api-utils": "^1.3.0"
},
"engines": {
- "node": "^16.0.0 || >=18.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
- "eslint": "^7.0.0 || ^8.0.0"
+ "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "eslint": "^8.57.0 || ^9.0.0"
},
"peerDependenciesMeta": {
"typescript": {
@@ -2243,26 +2505,26 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz",
- "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==",
+ "version": "8.0.0-alpha.16",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0-alpha.16.tgz",
+ "integrity": "sha512-L8eX2ggDQqb986+P9FZVsl/4M0vPplvgVzPkFFtPtsP2rVRSFpzGidZGzNN73RBq2G5KnWo87sx2mUrJ+99OZQ==",
"dev": true,
"dependencies": {
- "@typescript-eslint/scope-manager": "6.21.0",
- "@typescript-eslint/types": "6.21.0",
- "@typescript-eslint/typescript-estree": "6.21.0",
- "@typescript-eslint/visitor-keys": "6.21.0",
+ "@typescript-eslint/scope-manager": "8.0.0-alpha.16",
+ "@typescript-eslint/types": "8.0.0-alpha.16",
+ "@typescript-eslint/typescript-estree": "8.0.0-alpha.16",
+ "@typescript-eslint/visitor-keys": "8.0.0-alpha.16",
"debug": "^4.3.4"
},
"engines": {
- "node": "^16.0.0 || >=18.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^7.0.0 || ^8.0.0"
+ "eslint": "^8.57.0 || ^9.0.0"
},
"peerDependenciesMeta": {
"typescript": {
@@ -2271,16 +2533,16 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz",
- "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==",
+ "version": "8.0.0-alpha.16",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0-alpha.16.tgz",
+ "integrity": "sha512-SsN6Kf+sBK62CgDkW4XHZYDqCDwOY2d1Q4aUAOTcohhw06HiXYbY5xQ23GqOV2BL9TaKL+HuyyP+LLZ1aIG8FQ==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "6.21.0",
- "@typescript-eslint/visitor-keys": "6.21.0"
+ "@typescript-eslint/types": "8.0.0-alpha.16",
+ "@typescript-eslint/visitor-keys": "8.0.0-alpha.16"
},
"engines": {
- "node": "^16.0.0 || >=18.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -2288,26 +2550,23 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz",
- "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==",
+ "version": "8.0.0-alpha.16",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0-alpha.16.tgz",
+ "integrity": "sha512-g5GJ0sB6WLu71fkPlMe9JV1o3p6AKAN0vUfg4XGyYPLSElRYdMMy4Nuq1Snq2Gqs1rceomHrogp5v/qH7Iq7ig==",
"dev": true,
"dependencies": {
- "@typescript-eslint/typescript-estree": "6.21.0",
- "@typescript-eslint/utils": "6.21.0",
+ "@typescript-eslint/typescript-estree": "8.0.0-alpha.16",
+ "@typescript-eslint/utils": "8.0.0-alpha.16",
"debug": "^4.3.4",
- "ts-api-utils": "^1.0.1"
+ "ts-api-utils": "^1.3.0"
},
"engines": {
- "node": "^16.0.0 || >=18.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
- "peerDependencies": {
- "eslint": "^7.0.0 || ^8.0.0"
- },
"peerDependenciesMeta": {
"typescript": {
"optional": true
@@ -2315,12 +2574,12 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz",
- "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==",
+ "version": "8.0.0-alpha.16",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0-alpha.16.tgz",
+ "integrity": "sha512-06m3u1WIT49iYLK2GJWdT7Lmx54pX8imcW06AFnmgMXYDQsTZDdNXpHM6vwwL29LAWDv44j8g+eDPjJ4UNNiCA==",
"dev": true,
"engines": {
- "node": "^16.0.0 || >=18.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -2328,22 +2587,22 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz",
- "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==",
+ "version": "8.0.0-alpha.16",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0-alpha.16.tgz",
+ "integrity": "sha512-q5FvwPYGHmDF4/J7ssWMBHKDRY/3ar1PNoKTMYh/1foSCJ2e/Hv/GTuc63h03xi12IRyTn8R/M/56vH6qd+rSQ==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "6.21.0",
- "@typescript-eslint/visitor-keys": "6.21.0",
+ "@typescript-eslint/types": "8.0.0-alpha.16",
+ "@typescript-eslint/visitor-keys": "8.0.0-alpha.16",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
- "minimatch": "9.0.3",
- "semver": "^7.5.4",
- "ts-api-utils": "^1.0.1"
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
},
"engines": {
- "node": "^16.0.0 || >=18.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -2355,53 +2614,80 @@
}
}
},
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/@typescript-eslint/utils": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz",
- "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==",
+ "version": "8.0.0-alpha.16",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0-alpha.16.tgz",
+ "integrity": "sha512-u7mFyhJ4/jX7VaGieK+BC+PynvCH8fdr4Gie4RXO9bclvGAvMTzk62UZ65t90KN25M9/tvodxUoaZS4W4MQSNg==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
- "@types/json-schema": "^7.0.12",
- "@types/semver": "^7.5.0",
- "@typescript-eslint/scope-manager": "6.21.0",
- "@typescript-eslint/types": "6.21.0",
- "@typescript-eslint/typescript-estree": "6.21.0",
- "semver": "^7.5.4"
+ "@typescript-eslint/scope-manager": "8.0.0-alpha.16",
+ "@typescript-eslint/types": "8.0.0-alpha.16",
+ "@typescript-eslint/typescript-estree": "8.0.0-alpha.16"
},
"engines": {
- "node": "^16.0.0 || >=18.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^7.0.0 || ^8.0.0"
+ "eslint": "^8.57.0 || ^9.0.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz",
- "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==",
+ "version": "8.0.0-alpha.16",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0-alpha.16.tgz",
+ "integrity": "sha512-vSmfkS6FVBW1lhuf700XjcbQXtoXg3Aqbi+axsFYPNr/6oEkpLRonbKMxBzj4cGTnL/3sJl+gDVQSS7fVHWz3A==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "6.21.0",
- "eslint-visitor-keys": "^3.4.1"
+ "@typescript-eslint/types": "8.0.0-alpha.16",
+ "eslint-visitor-keys": "^3.4.3"
},
"engines": {
- "node": "^16.0.0 || >=18.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
- "node_modules/@ungap/structured-clone": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
- "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
- "dev": true
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
},
"node_modules/abbrev": {
"version": "1.1.1",
@@ -2471,9 +2757,9 @@
}
},
"node_modules/agent-base": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
- "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==",
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
+ "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
"dependencies": {
"debug": "^4.3.4"
},
@@ -2540,15 +2826,15 @@
}
},
"node_modules/ajv-formats/node_modules/ajv": {
- "version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+ "version": "8.14.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz",
+ "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==",
"dev": true,
"dependencies": {
- "fast-deep-equal": "^3.1.1",
+ "fast-deep-equal": "^3.1.3",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
- "uri-js": "^4.2.2"
+ "uri-js": "^4.4.1"
},
"funding": {
"type": "github",
@@ -2570,6 +2856,26 @@
"string-width": "^4.1.0"
}
},
+ "node_modules/ansi-align/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/ansi-align/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -2583,6 +2889,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "devOptional": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -2606,22 +2913,29 @@
"node": ">= 8"
}
},
+ "node_modules/anymatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/aproba": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
"integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==",
"devOptional": true
},
- "node_modules/archy": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
- "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==",
- "dev": true
- },
"node_modules/are-we-there-yet": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
"integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
+ "deprecated": "This package is no longer supported.",
"optional": true,
"dependencies": {
"delegates": "^1.0.0",
@@ -2696,9 +3010,12 @@
}
},
"node_modules/available-typed-arrays": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz",
- "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
"engines": {
"node": ">= 0.4"
},
@@ -2707,24 +3024,22 @@
}
},
"node_modules/avvio": {
- "version": "8.3.0",
- "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.3.0.tgz",
- "integrity": "sha512-VBVH0jubFr9LdFASy/vNtm5giTrnbVquWBhT0fyizuNK2rQ7e7ONU2plZQWUNqtE1EmxFEb+kbSkFRkstiaS9Q==",
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.3.2.tgz",
+ "integrity": "sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==",
"dev": true,
"dependencies": {
"@fastify/error": "^3.3.0",
- "archy": "^1.0.0",
- "debug": "^4.0.0",
"fastq": "^1.17.1"
}
},
"node_modules/axios": {
- "version": "1.6.5",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
- "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
+ "version": "1.6.8",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz",
+ "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==",
"dev": true,
"dependencies": {
- "follow-redirects": "^1.15.4",
+ "follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
@@ -2799,12 +3114,15 @@
}
},
"node_modules/binary-extensions": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
- "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"dev": true,
"engines": {
"node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/bl": {
@@ -2824,141 +3142,76 @@
"dev": true
},
"node_modules/bonjour-hap": {
- "version": "3.6.4",
- "resolved": "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.6.4.tgz",
- "integrity": "sha512-a76r95/qTAP5hOEZZhRoiosyFSVPPRSVev09Jh8yDf3JDKyrzELLf0vpQCuEXFueb9DcV9UJf2Jv3dktyuPBng==",
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.7.2.tgz",
+ "integrity": "sha512-BzOdOSIpXqjE1hejVNhj1T7E5YazPNG7cMOph5jQfzf1nF2yO18FSxuIg2zDMa4tFxhNC5d+U+0hT2bQkC5nTw==",
"dependencies": {
"array-flatten": "^2.1.2",
- "deep-equal": "^2.0.5",
- "ip": "^1.1.8",
+ "deep-equal": "^2.2.3",
"multicast-dns": "^7.2.5",
"multicast-dns-service-types": "^1.1.0"
}
},
"node_modules/bonjour-service": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz",
- "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz",
+ "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==",
"dev": true,
"dependencies": {
- "array-flatten": "^2.1.2",
- "dns-equal": "^1.0.0",
"fast-deep-equal": "^3.1.3",
"multicast-dns": "^7.2.5"
}
},
"node_modules/boxen": {
"version": "7.1.1",
- "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz",
- "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==",
- "dev": true,
- "dependencies": {
- "ansi-align": "^3.0.1",
- "camelcase": "^7.0.1",
- "chalk": "^5.2.0",
- "cli-boxes": "^3.0.0",
- "string-width": "^5.1.2",
- "type-fest": "^2.13.0",
- "widest-line": "^4.0.1",
- "wrap-ansi": "^8.1.0"
- },
- "engines": {
- "node": ">=14.16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/boxen/node_modules/ansi-regex": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
- "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
- "dev": true,
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
- }
- },
- "node_modules/boxen/node_modules/chalk": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
- "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
- "dev": true,
- "engines": {
- "node": "^12.17.0 || ^14.13 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/boxen/node_modules/emoji-regex": {
- "version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
- "dev": true
- },
- "node_modules/boxen/node_modules/string-width": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
- "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
- "dev": true,
- "dependencies": {
- "eastasianwidth": "^0.2.0",
- "emoji-regex": "^9.2.2",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/boxen/node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz",
+ "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==",
"dev": true,
"dependencies": {
- "ansi-regex": "^6.0.1"
+ "ansi-align": "^3.0.1",
+ "camelcase": "^7.0.1",
+ "chalk": "^5.2.0",
+ "cli-boxes": "^3.0.0",
+ "string-width": "^5.1.2",
+ "type-fest": "^2.13.0",
+ "widest-line": "^4.0.1",
+ "wrap-ansi": "^8.1.0"
},
"engines": {
- "node": ">=12"
+ "node": ">=14.16"
},
"funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/boxen/node_modules/type-fest": {
- "version": "2.19.0",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
- "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
+ "node_modules/boxen/node_modules/chalk": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+ "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
"dev": true,
"engines": {
- "node": ">=12.20"
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "devOptional": true,
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dependencies": {
- "balanced-match": "^1.0.0"
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
}
},
"node_modules/braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"dependencies": {
- "fill-range": "^7.0.1"
+ "fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
@@ -3021,19 +3274,10 @@
"node": ">=0.2.0"
}
},
- "node_modules/builtins": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz",
- "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==",
- "dev": true,
- "dependencies": {
- "semver": "^7.0.0"
- }
- },
"node_modules/cacache": {
- "version": "18.0.2",
- "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.2.tgz",
- "integrity": "sha512-r3NU8h/P+4lVUHfeRw1dtgQYar3DZMm4/cm2bZgOvrFC/su7budSOeqh52VJIC4U4iG1WWwV6vRW0znqBvxNuw==",
+ "version": "18.0.3",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.3.tgz",
+ "integrity": "sha512-qXCd4rh6I07cnDqh8V48/94Tc/WSfj+o3Gn6NZ0aZovS255bUx8O13uKxRFd2eWG0xgsco7+YItQNPaa5E85hg==",
"optional": true,
"dependencies": {
"@npmcli/fs": "^3.1.0",
@@ -3053,32 +3297,10 @@
"node": "^16.14.0 || >=18.0.0"
}
},
- "node_modules/cacache/node_modules/glob": {
- "version": "10.3.10",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
- "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
- "optional": true,
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^2.3.5",
- "minimatch": "^9.0.1",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
- "path-scurry": "^1.10.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/cacache/node_modules/lru-cache": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
- "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
+ "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
"optional": true,
"engines": {
"node": "14 || >=16.14"
@@ -3124,14 +3346,15 @@
}
},
"node_modules/call-bind": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.6.tgz",
- "integrity": "sha512-Mj50FLHtlsoVfRfnHaZvyrooHcrlceNZdL/QBvJJVd9Ta55qCQK0gs4ss2oZDeV9zFCs6ewzYgVE5yfVmfFpVg==",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"dependencies": {
+ "es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.3",
- "set-function-length": "^1.2.0"
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
@@ -3177,6 +3400,7 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -3255,14 +3479,14 @@
"dev": true
},
"node_modules/class-validator": {
- "version": "0.14.0",
- "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.0.tgz",
- "integrity": "sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==",
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz",
+ "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==",
"dev": true,
"dependencies": {
- "@types/validator": "^13.7.10",
- "libphonenumber-js": "^1.10.14",
- "validator": "^13.7.0"
+ "@types/validator": "^13.11.8",
+ "libphonenumber-js": "^1.10.53",
+ "validator": "^13.9.0"
}
},
"node_modules/clean-stack": {
@@ -3311,9 +3535,9 @@
}
},
"node_modules/cli-table3": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz",
- "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==",
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz",
+ "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==",
"dev": true,
"dependencies": {
"string-width": "^4.2.0"
@@ -3325,6 +3549,26 @@
"@colors/colors": "1.5.0"
}
},
+ "node_modules/cli-table3/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/cli-table3/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/clone": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
@@ -3338,6 +3582,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "devOptional": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -3348,7 +3593,8 @@
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "devOptional": true
},
"node_modules/color-support": {
"version": "1.1.3",
@@ -3372,12 +3618,12 @@
}
},
"node_modules/commander": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "version": "12.0.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz",
+ "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==",
"dev": true,
"engines": {
- "node": ">= 10"
+ "node": ">=18"
}
},
"node_modules/commist": {
@@ -3468,25 +3714,14 @@
}
},
"node_modules/cookie": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
- "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
+ "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
},
- "node_modules/core-js-pure": {
- "version": "3.35.1",
- "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.35.1.tgz",
- "integrity": "sha512-zcIdi/CL3MWbBJYo5YCeVAAx+Sy9yJE9I3/u9LkFABwbeaPhTMRWraM8mYFp9jW5Z50hOy7FVzCc8dCrpZqtIQ==",
- "dev": true,
- "hasInstallScript": true,
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/core-js"
- }
- },
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -3588,15 +3823,15 @@
}
},
"node_modules/decamelize": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-3.2.0.tgz",
- "integrity": "sha512-4TgkVUsmmu7oCSyGBm5FvfMoACuoh9EOidm7V5/J2X2djAwwt57qb3F2KMP2ITqODTCSwb+YRV+0Zqrv18k/hw==",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz",
+ "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==",
"dev": true,
- "dependencies": {
- "xregexp": "^4.2.4"
- },
"engines": {
- "node": ">=6"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/decompress-response": {
@@ -3691,17 +3926,19 @@
}
},
"node_modules/define-data-property": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.2.tgz",
- "integrity": "sha512-SRtsSqsDbgpJBbW3pABMCOt6rQyeM8s8RiyeSN8jYG8sYmt/kGJejbydttUsnDs1tadr19tvhT4ShwMyoqAm4g==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dependencies": {
+ "es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.2",
- "gopd": "^1.0.1",
- "has-property-descriptors": "^1.0.1"
+ "gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/define-properties": {
@@ -3745,9 +3982,9 @@
}
},
"node_modules/detect-libc": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
- "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
+ "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
"devOptional": true,
"engines": {
"node": ">=8"
@@ -3774,12 +4011,6 @@
"node": ">=8"
}
},
- "node_modules/dns-equal": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
- "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==",
- "dev": true
- },
"node_modules/dns-packet": {
"version": "5.6.1",
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz",
@@ -3791,18 +4022,6 @@
"node": ">=6"
}
},
- "node_modules/doctrine": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
- "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
- "dev": true,
- "dependencies": {
- "esutils": "^2.0.2"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/dot-prop": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
@@ -3870,14 +4089,14 @@
}
},
"node_modules/duplexify": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz",
- "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==",
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
+ "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
"dependencies": {
"end-of-stream": "^1.4.1",
"inherits": "^2.0.3",
"readable-stream": "^3.1.1",
- "stream-shift": "^1.0.0"
+ "stream-shift": "^1.0.2"
}
},
"node_modules/eastasianwidth": {
@@ -3895,9 +4114,9 @@
}
},
"node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
"devOptional": true
},
"node_modules/encoding": {
@@ -3992,6 +4211,17 @@
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
"devOptional": true
},
+ "node_modules/es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dependencies": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
@@ -4050,41 +4280,37 @@
}
},
"node_modules/eslint": {
- "version": "8.56.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz",
- "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==",
+ "version": "9.3.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.3.0.tgz",
+ "integrity": "sha512-5Iv4CsZW030lpUqHBapdPo3MJetAPtejVW8B84GIcIIv8+ohFaddXsrn1Gn8uD9ijDb+kcYKFUVmC8qG8B2ORQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
- "@eslint/eslintrc": "^2.1.4",
- "@eslint/js": "8.56.0",
- "@humanwhocodes/config-array": "^0.11.13",
+ "@eslint/eslintrc": "^3.1.0",
+ "@eslint/js": "9.3.0",
+ "@humanwhocodes/config-array": "^0.13.0",
"@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.3.0",
"@nodelib/fs.walk": "^1.2.8",
- "@ungap/structured-clone": "^1.2.0",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
- "doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.2.2",
- "eslint-visitor-keys": "^3.4.3",
- "espree": "^9.6.1",
+ "eslint-scope": "^8.0.1",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.0.1",
"esquery": "^1.4.2",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^6.0.1",
+ "file-entry-cache": "^8.0.0",
"find-up": "^5.0.0",
"glob-parent": "^6.0.2",
- "globals": "^13.19.0",
- "graphemer": "^1.4.0",
"ignore": "^5.2.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"is-path-inside": "^3.0.3",
- "js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash.merge": "^4.6.2",
@@ -4098,74 +4324,52 @@
"eslint": "bin/eslint.js"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-scope": {
- "version": "7.2.2",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
- "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz",
+ "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==",
"dev": true,
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
+ "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
"dev": true,
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/eslint/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/espree": {
- "version": "9.6.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
- "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz",
+ "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==",
"dev": true,
"dependencies": {
- "acorn": "^8.9.0",
+ "acorn": "^8.11.3",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.1"
+ "eslint-visitor-keys": "^4.0.0"
},
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -4345,14 +4549,14 @@
"dev": true
},
"node_modules/fast-json-stringify": {
- "version": "5.12.0",
- "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.12.0.tgz",
- "integrity": "sha512-7Nnm9UPa7SfHRbHVA1kJQrGXCRzB7LMlAAqHXQFkEQqueJm1V8owm0FsE/2Do55/4CcdhwiLQERaKomOnKQkyA==",
+ "version": "5.16.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.16.0.tgz",
+ "integrity": "sha512-A4bg6E15QrkuVO3f0SwIASgzMzR6XC4qTyTqhf3hYXy0iazbAdZKwkE+ox4WgzKyzM6ygvbdq3r134UjOaaAnA==",
"dev": true,
"dependencies": {
"@fastify/merge-json-schemas": "^0.1.0",
"ajv": "^8.10.0",
- "ajv-formats": "^2.1.1",
+ "ajv-formats": "^3.0.1",
"fast-deep-equal": "^3.1.3",
"fast-uri": "^2.1.0",
"json-schema-ref-resolver": "^1.0.1",
@@ -4360,21 +4564,38 @@
}
},
"node_modules/fast-json-stringify/node_modules/ajv": {
- "version": "8.12.0",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
- "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+ "version": "8.14.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz",
+ "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==",
"dev": true,
"dependencies": {
- "fast-deep-equal": "^3.1.1",
+ "fast-deep-equal": "^3.1.3",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
- "uri-js": "^4.2.2"
+ "uri-js": "^4.4.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
+ "node_modules/fast-json-stringify/node_modules/ajv-formats": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
"node_modules/fast-json-stringify/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
@@ -4403,9 +4624,9 @@
}
},
"node_modules/fast-redact": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.3.0.tgz",
- "integrity": "sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ==",
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz",
+ "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==",
"dev": true,
"engines": {
"node": ">=6"
@@ -4433,19 +4654,29 @@
"dev": true
},
"node_modules/fastify": {
- "version": "4.25.1",
- "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.25.1.tgz",
- "integrity": "sha512-D8d0rv61TwqoAS7lom2tvIlgVMlx88lLsiwXyWNjA7CU/LC/mx/Gp2WAlC0S/ABq19U+y/aRvYFG5xLUu2aMrg==",
+ "version": "4.26.2",
+ "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.26.2.tgz",
+ "integrity": "sha512-90pjTuPGrfVKtdpLeLzND5nyC4woXZN5VadiNQCicj/iJU4viNHKhsAnb7jmv1vu2IzkLXyBiCzdWuzeXgQ5Ug==",
"dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
"dependencies": {
"@fastify/ajv-compiler": "^3.5.0",
"@fastify/error": "^3.4.0",
"@fastify/fast-json-stringify-compiler": "^4.3.0",
"abstract-logging": "^2.0.1",
- "avvio": "^8.2.1",
+ "avvio": "^8.3.0",
"fast-content-type-parse": "^1.1.0",
"fast-json-stringify": "^5.8.0",
- "find-my-way": "^7.7.0",
+ "find-my-way": "^8.0.0",
"light-my-request": "^5.11.0",
"pino": "^8.17.0",
"process-warning": "^3.0.0",
@@ -4472,21 +4703,21 @@
}
},
"node_modules/file-entry-cache": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
- "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
"dev": true,
"dependencies": {
- "flat-cache": "^3.0.4"
+ "flat-cache": "^4.0.0"
},
"engines": {
- "node": "^10.12.0 || >=12.0.0"
+ "node": ">=16.0.0"
}
},
"node_modules/fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
@@ -4496,14 +4727,14 @@
}
},
"node_modules/find-my-way": {
- "version": "7.7.0",
- "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.7.0.tgz",
- "integrity": "sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==",
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.2.0.tgz",
+ "integrity": "sha512-HdWXgFYc6b1BJcOBDBwjqWuHJj1WYiqrxSh25qtU4DabpMFdj/gSunNBQb83t+8Zt67D7CXEzJWTkxaShMTMOA==",
"dev": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-querystring": "^1.0.0",
- "safe-regex2": "^2.0.0"
+ "safe-regex2": "^3.1.0"
},
"engines": {
"node": ">=14"
@@ -4516,96 +4747,38 @@
"dev": true,
"dependencies": {
"locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/flat-cache": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
- "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
- "dev": true,
- "dependencies": {
- "flatted": "^3.2.9",
- "keyv": "^4.5.3",
- "rimraf": "^3.0.2"
- },
- "engines": {
- "node": "^10.12.0 || >=12.0.0"
- }
- },
- "node_modules/flat-cache/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/flat-cache/node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "dev": true,
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/flat-cache/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
+ "path-exists": "^4.0.0"
},
"engines": {
- "node": "*"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/flat-cache/node_modules/rimraf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
- "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
"dev": true,
"dependencies": {
- "glob": "^7.1.3"
- },
- "bin": {
- "rimraf": "bin.js"
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
},
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
+ "engines": {
+ "node": ">=16"
}
},
"node_modules/flatted": {
- "version": "3.2.9",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz",
- "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
"dev": true
},
"node_modules/follow-redirects": {
- "version": "1.15.5",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
- "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"dev": true,
"funding": [
{
@@ -4646,18 +4819,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/foreground-child/node_modules/signal-exit": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
- "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
- "devOptional": true,
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
@@ -4712,9 +4873,9 @@
"dev": true
},
"node_modules/fs-extra": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
- "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
+ "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.0",
@@ -4722,7 +4883,7 @@
"universalify": "^2.0.0"
},
"engines": {
- "node": ">=12"
+ "node": ">=14.14"
}
},
"node_modules/fs-minipass": {
@@ -4760,6 +4921,7 @@
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
"integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
+ "deprecated": "This package is no longer supported.",
"dev": true,
"dependencies": {
"graceful-fs": "^4.1.2",
@@ -4771,20 +4933,11 @@
"node": ">=0.6"
}
},
- "node_modules/fstream/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/fstream/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
@@ -4801,22 +4954,11 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/fstream/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/fstream/node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"dependencies": {
"glob": "^7.1.3"
@@ -4842,9 +4984,9 @@
}
},
"node_modules/futoin-hkdf": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/futoin-hkdf/-/futoin-hkdf-1.4.3.tgz",
- "integrity": "sha512-K4MIe2xSVRMYxsA4w0ap5fp1C2hA9StA2Ad1JZHX57VMCdHIRB5BSrd1FhuadTQG9MkjggaTCrw7v5XXFyY3/w==",
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/futoin-hkdf/-/futoin-hkdf-1.5.3.tgz",
+ "integrity": "sha512-SewY5KdMpaoCeh7jachEWFsh1nNlaDjNHZXWqL5IGwtpEYHTgkr2+AMCgNwKWkcc0wpSYrZfR7he4WdmHFtDxQ==",
"dev": true,
"engines": {
"node": ">=8"
@@ -4854,6 +4996,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
"integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
+ "deprecated": "This package is no longer supported.",
"optional": true,
"dependencies": {
"aproba": "^1.0.3 || ^2.0.0",
@@ -4870,15 +5013,42 @@
"node": ">=10"
}
},
+ "node_modules/gauge/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "optional": true
+ },
+ "node_modules/gauge/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "optional": true
+ },
+ "node_modules/gauge/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "optional": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/gaxios": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.2.0.tgz",
- "integrity": "sha512-H6+bHeoEAU5D6XNc6mPKeN5dLZqEDs9Gpk6I+SZBEzK5So58JVrHPmevNi35fRl1J9Y5TaeLW0kYx3pCJ1U2mQ==",
+ "version": "6.6.0",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.6.0.tgz",
+ "integrity": "sha512-bpOZVQV5gthH/jVCSuYuokRo2bTKOcuBiVWpjmTn6C5Agl5zclGfTljuGsQZxwwDBkli+YhZhP4TdlqTnhOezQ==",
"dependencies": {
"extend": "^3.0.2",
"https-proxy-agent": "^7.0.1",
"is-stream": "^2.0.0",
- "node-fetch": "^2.6.9"
+ "node-fetch": "^2.6.9",
+ "uuid": "^9.0.1"
},
"engines": {
"node": ">=14"
@@ -4945,19 +5115,22 @@
"dev": true
},
"node_modules/glob": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
- "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
- "dev": true,
+ "version": "10.4.1",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz",
+ "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==",
+ "devOptional": true,
"dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^5.0.1",
- "once": "^1.3.0"
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
},
"engines": {
- "node": ">=12"
+ "node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -4975,16 +5148,28 @@
"node": ">=10.13.0"
}
},
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "devOptional": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
"node_modules/glob/node_modules/minimatch": {
- "version": "5.1.6",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
- "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
- "dev": true,
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
+ "devOptional": true,
"dependencies": {
"brace-expansion": "^2.0.1"
},
"engines": {
- "node": ">=10"
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/global-dirs": {
@@ -5012,15 +5197,12 @@
}
},
"node_modules/globals": {
- "version": "13.24.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
- "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "version": "15.3.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.3.0.tgz",
+ "integrity": "sha512-cCdyVjIUVTtX8ZsPkq1oCsOsLmGIswqnjZYMJJTGaNApj1yHtLSymKhwH51ttirREn75z3p4k051clwg7rvNKA==",
"dev": true,
- "dependencies": {
- "type-fest": "^0.20.2"
- },
"engines": {
- "node": ">=8"
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -5047,9 +5229,9 @@
}
},
"node_modules/google-auth-library": {
- "version": "9.6.3",
- "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.6.3.tgz",
- "integrity": "sha512-4CacM29MLC2eT9Cey5GDVK4Q8t+MMp8+OEdOaqD9MG6b0dOyLORaaeJMPQ7EESVgm/+z5EKYyFLxgzBJlJgyHQ==",
+ "version": "9.10.0",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.10.0.tgz",
+ "integrity": "sha512-ol+oSa5NbcGdDqA+gZ3G3mev59OHBZksBTxY/tYwjtcp1H/scAFwJfSQU9/1RALoyZ7FslNbke8j4i3ipwlyuQ==",
"dependencies": {
"base64-js": "^1.3.0",
"ecdsa-sig-formatter": "^1.0.11",
@@ -5063,9 +5245,9 @@
}
},
"node_modules/googleapis": {
- "version": "133.0.0",
- "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-133.0.0.tgz",
- "integrity": "sha512-6xyc49j+x7N4smawJs/q1i7mbSkt6SYUWWd9RbsmmDW7gRv+mhwZ4xT+XkPihZcNyo/diF//543WZq4szdS74w==",
+ "version": "137.1.0",
+ "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-137.1.0.tgz",
+ "integrity": "sha512-2L7SzN0FLHyQtFmyIxrcXhgust77067pkkduqkbIpDuj9JzVnByxsRrcRfUMFQam3rQkWW2B0f1i40IwKDWIVQ==",
"dependencies": {
"google-auth-library": "^9.0.0",
"googleapis-common": "^7.0.0"
@@ -5075,13 +5257,13 @@
}
},
"node_modules/googleapis-common": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.0.1.tgz",
- "integrity": "sha512-mgt5zsd7zj5t5QXvDanjWguMdHAcJmmDrF9RkInCecNsyV7S7YtGqm5v2IWONNID88osb7zmx5FtrAP12JfD0w==",
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.2.0.tgz",
+ "integrity": "sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==",
"dependencies": {
"extend": "^3.0.2",
"gaxios": "^6.0.3",
- "google-auth-library": "^9.0.0",
+ "google-auth-library": "^9.7.0",
"qs": "^6.7.0",
"url-template": "^2.0.8",
"uuid": "^9.0.0"
@@ -5151,24 +5333,24 @@
}
},
"node_modules/hap-nodejs": {
- "version": "0.11.1",
- "resolved": "https://registry.npmjs.org/hap-nodejs/-/hap-nodejs-0.11.1.tgz",
- "integrity": "sha512-hJuGyjng2jlzhZsviWCldaokT7l7BE3iGmWdlE6DNmQFDTmiBN3deNksAZ2nt7qp5jYEv7ZUvW7WBZqJsLh3ww==",
+ "version": "0.12.1",
+ "resolved": "https://registry.npmjs.org/hap-nodejs/-/hap-nodejs-0.12.1.tgz",
+ "integrity": "sha512-iUUMaK6ucDKLMjT4m5Oz6CoLKkGg+omI6GR96weyL8fPGR1HYoCMtoJoUNW2NSIp4b2A6hx4zjNOEtLEaTA2MQ==",
"dev": true,
"dependencies": {
- "@homebridge/ciao": "^1.1.5",
- "@homebridge/dbus-native": "^0.5.1",
- "bonjour-hap": "~3.6.4",
+ "@homebridge/ciao": "^1.2.0",
+ "@homebridge/dbus-native": "^0.6.0",
+ "bonjour-hap": "^3.7.2",
"debug": "^4.3.4",
- "fast-srp-hap": "~2.0.4",
- "futoin-hkdf": "~1.4.3",
+ "fast-srp-hap": "^2.0.4",
+ "futoin-hkdf": "^1.5.3",
"node-persist": "^0.0.11",
"source-map-support": "^0.5.21",
- "tslib": "^2.4.0",
+ "tslib": "^2.6.2",
"tweetnacl": "^1.0.3"
},
"engines": {
- "node": ">=10.17.0"
+ "node": "^18 || ^20"
}
},
"node_modules/has-bigints": {
@@ -5183,25 +5365,26 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
"engines": {
"node": ">=8"
}
},
"node_modules/has-property-descriptors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
- "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dependencies": {
- "get-intrinsic": "^1.2.2"
+ "es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-proto": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
- "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
+ "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
"engines": {
"node": ">= 0.4"
},
@@ -5253,9 +5436,9 @@
}
},
"node_modules/hasown": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
- "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"dependencies": {
"function-bind": "^1.1.2"
},
@@ -5264,13 +5447,13 @@
}
},
"node_modules/hb-lib-tools": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-1.2.2.tgz",
- "integrity": "sha512-xFU9vxH4Ap3GoZ1zRBYGNCGvRz6lFh0ylYx0QaoYKcydVLHQKleSJX9g3X/YmgcQNpfj4XFWT7uDB/XDZeW4Nw==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/hb-lib-tools/-/hb-lib-tools-2.0.1.tgz",
+ "integrity": "sha512-okOzIvlR4QgfyCh7qIOczRxJDuv3TdzvWTMFlYK8c4UTSXHnIb/dWBvkud7tQyXa9n8FA2DBTJdAUSWJLM63sA==",
"dependencies": {
- "bonjour-hap": "^3.6.4",
- "chalk": "^4.1.2",
- "semver": "^7.5.4"
+ "bonjour-hap": "^3.7.2",
+ "chalk": "^5.3.0",
+ "semver": "^7.6.2"
},
"bin": {
"hap": "cli/hap.js",
@@ -5279,7 +5462,18 @@
"upnp": "cli/upnp.js"
},
"engines": {
- "node": "20.11.0||^20||^18"
+ "node": "20.13.1||^20||^18"
+ }
+ },
+ "node_modules/hb-lib-tools/node_modules/chalk": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+ "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/helmet": {
@@ -5300,19 +5494,11 @@
"readable-stream": "^3.6.0"
}
},
- "node_modules/help-me/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/help-me/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -5328,39 +5514,31 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/help-me/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/hexy": {
- "version": "0.2.11",
- "resolved": "https://registry.npmjs.org/hexy/-/hexy-0.2.11.tgz",
- "integrity": "sha512-ciq6hFsSG/Bpt2DmrZJtv+56zpPdnq+NQ4ijEFrveKN0ZG1mhl/LdT1NQZ9se6ty1fACcI4d4vYqC9v8EYpH2A==",
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/hexy/-/hexy-0.3.5.tgz",
+ "integrity": "sha512-UCP7TIZPXz5kxYJnNOym+9xaenxCLor/JyhKieo8y8/bJWunGh9xbhy3YrgYJUQ87WwfXGm05X330DszOfINZw==",
"dev": true,
"bin": {
"hexy": "bin/hexy_cmd.js"
+ },
+ "engines": {
+ "node": ">=10.4"
}
},
"node_modules/homebridge": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/homebridge/-/homebridge-1.7.0.tgz",
- "integrity": "sha512-2QikXnmpnFe2s33Q8TeYE5+sXyKHUZ+9l5WfDmpuupHdct6H/G6b6z3HCj+2rlMRKKY5ElLv5XtLoxOcafnL0g==",
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/homebridge/-/homebridge-1.8.2.tgz",
+ "integrity": "sha512-K0P9/qk3RdAKGLhGrmtF4skUjcygNlnBu0S/ssKIdp4p0kMzW2wjw2Q+z7TCxgZVy84/kaR09UD1n6uJAunTOQ==",
"dev": true,
"dependencies": {
- "chalk": "^4.1.2",
- "commander": "^7.2.0",
- "fs-extra": "^10.1.0",
- "hap-nodejs": "~0.11.1",
- "qrcode-terminal": "^0.12.0",
- "semver": "^7.5.4",
- "source-map-support": "^0.5.21"
+ "chalk": "4.1.2",
+ "commander": "12.0.0",
+ "fs-extra": "11.2.0",
+ "hap-nodejs": "0.12.1",
+ "qrcode-terminal": "0.12.0",
+ "semver": "7.6.2",
+ "source-map-support": "0.5.21"
},
"bin": {
"homebridge": "bin/homebridge"
@@ -5370,9 +5548,9 @@
}
},
"node_modules/homebridge-config-ui-x": {
- "version": "4.55.1",
- "resolved": "https://registry.npmjs.org/homebridge-config-ui-x/-/homebridge-config-ui-x-4.55.1.tgz",
- "integrity": "sha512-XojwCOEB6LsJeYt6NFakXGCIs9WFA/xiraVhC1R1+//bQPmv1T1F3sZvrPxp1UTBLKxoxT37SMdtq0us8pRyaw==",
+ "version": "4.56.2",
+ "resolved": "https://registry.npmjs.org/homebridge-config-ui-x/-/homebridge-config-ui-x-4.56.2.tgz",
+ "integrity": "sha512-jsWr/EgxUkxznlQ1UxUjZWYc+xl99tWj6n5qqL7Xpe683qjYv5hA71Bj75v6ETwrz2sPttUb2sXMUIZDNvxtDA==",
"dev": true,
"funding": [
{
@@ -5386,28 +5564,27 @@
],
"dependencies": {
"@fastify/helmet": "11.1.1",
- "@fastify/multipart": "8.0.0",
- "@fastify/static": "6.12.0",
+ "@fastify/multipart": "8.2.0",
+ "@fastify/static": "7.0.2",
+ "@homebridge/hap-client": "1.10.0",
"@homebridge/node-pty-prebuilt-multiarch": "0.11.12",
- "@nestjs/axios": "3.0.1",
- "@nestjs/common": "10.3.0",
- "@nestjs/core": "10.3.0",
+ "@nestjs/axios": "3.0.2",
+ "@nestjs/common": "10.3.7",
+ "@nestjs/core": "10.3.7",
"@nestjs/jwt": "10.2.0",
"@nestjs/passport": "10.0.3",
- "@nestjs/platform-fastify": "10.3.0",
- "@nestjs/platform-socket.io": "10.3.0",
- "@nestjs/swagger": "7.1.17",
- "@nestjs/websockets": "10.3.0",
- "@oznu/hap-client": "1.9.0",
- "axios": "1.6.5",
+ "@nestjs/platform-fastify": "10.3.7",
+ "@nestjs/platform-socket.io": "10.3.7",
+ "@nestjs/swagger": "7.3.1",
+ "@nestjs/websockets": "10.3.7",
+ "axios": "1.6.8",
"bash-color": "0.0.4",
- "bonjour-service": "=1.1.1",
"buffer-shims": "1.0.0",
"class-transformer": "0.5.1",
- "class-validator": "0.14.0",
- "commander": "11.1.0",
+ "class-validator": "0.14.1",
+ "commander": "12.0.0",
"dayjs": "1.11.10",
- "fastify": "4.25.1",
+ "fastify": "4.26.2",
"fs-extra": "11.2.0",
"jsonwebtoken": "9.0.2",
"lodash": "4.17.21",
@@ -5418,12 +5595,12 @@
"p-limit": "3.1.0",
"passport": "0.7.0",
"passport-jwt": "4.0.1",
- "reflect-metadata": "0.1.14",
+ "reflect-metadata": "0.2.2",
"rxjs": "7.8.1",
- "semver": "7.5.4",
- "systeminformation": "5.21.22",
+ "semver": "7.6.0",
+ "systeminformation": "5.22.7",
"tail": "2.2.6",
- "tar": "6.2.0",
+ "tar": "6.2.1",
"tcp-port-used": "1.0.2",
"unzipper": "0.10.14"
},
@@ -5436,33 +5613,10 @@
"node": "^18 || ^20"
}
},
- "node_modules/homebridge-config-ui-x/node_modules/commander": {
- "version": "11.1.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
- "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
- "dev": true,
- "engines": {
- "node": ">=16"
- }
- },
- "node_modules/homebridge-config-ui-x/node_modules/fs-extra": {
- "version": "11.2.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
- "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
- "dev": true,
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=14.14"
- }
- },
"node_modules/homebridge-config-ui-x/node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
@@ -5475,12 +5629,12 @@
}
},
"node_modules/homebridge-lib": {
- "version": "6.7.3",
- "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-6.7.3.tgz",
- "integrity": "sha512-1G2FR3T+/GpRPYD2JU4dJWgf2OFbX/5dzx8doyVS9htkSbZWN4l+rXSVVU2K2KBidnj3NNOVBj21uw/20XGOgQ==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/homebridge-lib/-/homebridge-lib-7.0.1.tgz",
+ "integrity": "sha512-OMwQEx1Uhb67VsFoY+s9KXUjc6g7XQP86GrNEvPD7ldnaA/D0HN3KHfsaJ0JyZaNCvUOEqG8A4youCMj73RSSw==",
"dependencies": {
- "@homebridge/plugin-ui-utils": "~1.0.0",
- "hb-lib-tools": "~1.2.1"
+ "@homebridge/plugin-ui-utils": "~1.0.3",
+ "hb-lib-tools": "~2.0.1"
},
"bin": {
"hap": "cli/hap.js",
@@ -5489,8 +5643,8 @@
"upnp": "cli/upnp.js"
},
"engines": {
- "homebridge": "^1.7.0",
- "node": "20.10.0||^20||^18"
+ "homebridge": "^1.8.1",
+ "node": "20.13.1||^20||^18"
}
},
"node_modules/hosted-git-info": {
@@ -5537,9 +5691,9 @@
}
},
"node_modules/http-proxy-agent": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz",
- "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==",
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"optional": true,
"dependencies": {
"agent-base": "^7.1.0",
@@ -5563,9 +5717,9 @@
}
},
"node_modules/https-proxy-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz",
- "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==",
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz",
+ "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==",
"dependencies": {
"agent-base": "^7.0.2",
"debug": "4"
@@ -5630,9 +5784,9 @@
"dev": true
},
"node_modules/ignore-walk": {
- "version": "6.0.4",
- "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.4.tgz",
- "integrity": "sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==",
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz",
+ "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==",
"dev": true,
"dependencies": {
"minimatch": "^9.0.0"
@@ -5641,6 +5795,30 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
+ "node_modules/ignore-walk/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/ignore-walk/node_modules/minimatch": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -5691,18 +5869,19 @@
"dev": true
},
"node_modules/inflection": {
- "version": "1.13.4",
- "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz",
- "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/inflection/-/inflection-3.0.0.tgz",
+ "integrity": "sha512-1zEJU1l19SgJlmwqsEyFTbScw/tkMHFenUo//Y0i+XEP83gDFdMvPizAD/WGcE+l1ku12PcTVHQhO6g5E0UCMw==",
"dev": true,
- "engines": [
- "node >= 0.4.0"
- ]
+ "engines": {
+ "node": ">=18.0.0"
+ }
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
@@ -5714,9 +5893,9 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ini": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz",
- "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==",
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz",
+ "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==",
"dev": true,
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
@@ -5735,11 +5914,6 @@
"node": ">= 0.4"
}
},
- "node_modules/ip": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
- "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg=="
- },
"node_modules/ip-address": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
@@ -5950,9 +6124,12 @@
"devOptional": true
},
"node_modules/is-map": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
- "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "engines": {
+ "node": ">= 0.4"
+ },
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -6026,19 +6203,25 @@
}
},
"node_modules/is-set": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz",
- "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "engines": {
+ "node": ">= 0.4"
+ },
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-shared-array-buffer": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
- "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==",
"dependencies": {
- "call-bind": "^1.0.2"
+ "call-bind": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -6108,20 +6291,26 @@
"dev": true
},
"node_modules/is-weakmap": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
- "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "engines": {
+ "node": ">= 0.4"
+ },
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-weakset": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz",
- "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz",
+ "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==",
"dependencies": {
- "call-bind": "^1.0.2",
- "get-intrinsic": "^1.1.1"
+ "call-bind": "^1.0.7",
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -6171,9 +6360,9 @@
}
},
"node_modules/jackspeak": {
- "version": "2.3.6",
- "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
- "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz",
+ "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==",
"devOptional": true,
"dependencies": {
"@isaacs/cliui": "^8.0.2"
@@ -6236,9 +6425,9 @@
"dev": true
},
"node_modules/json-parse-even-better-errors": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz",
- "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==",
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz",
+ "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==",
"dev": true,
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
@@ -6259,24 +6448,7 @@
"integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==",
"dev": true,
"dependencies": {
- "fast-deep-equal": "^3.1.3"
- }
- },
- "node_modules/json-schema-resolver": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/json-schema-resolver/-/json-schema-resolver-2.0.0.tgz",
- "integrity": "sha512-pJ4XLQP4Q9HTxl6RVDLJ8Cyh1uitSs0CzDBAz1uoJ4sRD/Bk7cFSXL1FUXDW3zJ7YnfliJx6eu8Jn283bpZ4Yg==",
- "dev": true,
- "dependencies": {
- "debug": "^4.1.1",
- "rfdc": "^1.1.4",
- "uri-js": "^4.2.2"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/Eomm/json-schema-resolver?sponsor=1"
+ "fast-deep-equal": "^3.1.3"
}
},
"node_modules/json-schema-traverse": {
@@ -6447,28 +6619,22 @@
}
},
"node_modules/libphonenumber-js": {
- "version": "1.10.55",
- "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.55.tgz",
- "integrity": "sha512-MrTg2JFLscgmTY6/oT9vopYETlgUls/FU6OaeeamGwk4LFxjIgOUML/ZSZICgR0LPYXaonVJo40lzMvaaTJlQA==",
+ "version": "1.11.2",
+ "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.2.tgz",
+ "integrity": "sha512-V9mGLlaXN1WETzqQvSu6qf6XVAr3nFuJvWsHcuzCCCo6xUKawwSxOPTpan5CGOSKTn5w/bQuCZcLPJkyysgC3w==",
"dev": true
},
"node_modules/light-my-request": {
- "version": "5.11.0",
- "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.11.0.tgz",
- "integrity": "sha512-qkFCeloXCOMpmEdZ/MV91P8AT4fjwFXWaAFz3lUeStM8RcoM1ks4J/F8r1b3r6y/H4u3ACEJ1T+Gv5bopj7oDA==",
+ "version": "5.12.0",
+ "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.12.0.tgz",
+ "integrity": "sha512-P526OX6E7aeCIfw/9UyJNsAISfcFETghysaWHQAlQYayynShT08MOj4c6fBCvTWBrHXSvqBAKDp3amUPSCQI4w==",
"dev": true,
"dependencies": {
- "cookie": "^0.5.0",
- "process-warning": "^2.0.0",
+ "cookie": "^0.6.0",
+ "process-warning": "^3.0.0",
"set-cookie-parser": "^2.4.1"
}
},
- "node_modules/light-my-request/node_modules/process-warning": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz",
- "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==",
- "dev": true
- },
"node_modules/listenercount": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz",
@@ -6629,9 +6795,9 @@
"dev": true
},
"node_modules/make-fetch-happen": {
- "version": "13.0.0",
- "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz",
- "integrity": "sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==",
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz",
+ "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==",
"optional": true,
"dependencies": {
"@npmcli/agent": "^2.0.0",
@@ -6643,6 +6809,7 @@
"minipass-flush": "^1.0.5",
"minipass-pipeline": "^1.2.4",
"negotiator": "^0.6.3",
+ "proc-log": "^4.2.0",
"promise-retry": "^2.0.1",
"ssri": "^10.0.0"
},
@@ -6650,6 +6817,15 @@
"node": "^16.14.0 || >=18.0.0"
}
},
+ "node_modules/make-fetch-happen/node_modules/proc-log": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz",
+ "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==",
+ "optional": true,
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
"node_modules/map-stream": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz",
@@ -6666,18 +6842,30 @@
}
},
"node_modules/micromatch": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
- "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz",
+ "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==",
"dev": true,
"dependencies": {
- "braces": "^3.0.2",
+ "braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
"node": ">=8.6"
}
},
+ "node_modules/micromatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/mime": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
@@ -6733,18 +6921,14 @@
}
},
"node_modules/minimatch": {
- "version": "9.0.3",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
- "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
- "devOptional": true,
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
- "brace-expansion": "^2.0.1"
+ "brace-expansion": "^1.1.7"
},
"engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
+ "node": "*"
}
},
"node_modules/minimist": {
@@ -6756,9 +6940,9 @@
}
},
"node_modules/minipass": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
- "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"devOptional": true,
"engines": {
"node": ">=16 || 14 >=14.17"
@@ -6777,9 +6961,9 @@
}
},
"node_modules/minipass-fetch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz",
- "integrity": "sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==",
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz",
+ "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==",
"devOptional": true,
"dependencies": {
"minipass": "^7.0.3",
@@ -6931,9 +7115,9 @@
"dev": true
},
"node_modules/mnemonist": {
- "version": "0.39.5",
- "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.5.tgz",
- "integrity": "sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==",
+ "version": "0.39.6",
+ "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.6.tgz",
+ "integrity": "sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==",
"dev": true,
"dependencies": {
"obliterator": "^2.0.1"
@@ -7004,9 +7188,9 @@
"integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ=="
},
"node_modules/nan": {
- "version": "2.18.0",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz",
- "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==",
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz",
+ "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==",
"devOptional": true
},
"node_modules/napi-build-utils": {
@@ -7037,9 +7221,9 @@
}
},
"node_modules/node-abi": {
- "version": "3.54.0",
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.54.0.tgz",
- "integrity": "sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA==",
+ "version": "3.62.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.62.0.tgz",
+ "integrity": "sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g==",
"dev": true,
"dependencies": {
"semver": "^7.3.5"
@@ -7086,9 +7270,9 @@
}
},
"node_modules/node-gyp": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.0.1.tgz",
- "integrity": "sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==",
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.1.0.tgz",
+ "integrity": "sha512-B4J5M1cABxPc5PwfjhbV5hoy2DP9p8lFXASnEN6hugXOa61416tnTZ29x9sSwAd0o99XNIcpvDDy1swAExsVKA==",
"optional": true,
"dependencies": {
"env-paths": "^2.2.0",
@@ -7110,9 +7294,9 @@
}
},
"node_modules/node-gyp-build": {
- "version": "4.8.0",
- "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz",
- "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==",
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz",
+ "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==",
"optional": true,
"bin": {
"node-gyp-build": "bin.js",
@@ -7129,28 +7313,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
- "node_modules/node-gyp/node_modules/glob": {
- "version": "10.3.10",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
- "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
- "optional": true,
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^2.3.5",
- "minimatch": "^9.0.1",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
- "path-scurry": "^1.10.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/node-gyp/node_modules/isexe": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
@@ -7161,9 +7323,9 @@
}
},
"node_modules/node-gyp/node_modules/nopt": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz",
- "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==",
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz",
+ "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==",
"optional": true,
"dependencies": {
"abbrev": "^2.0.0"
@@ -7215,21 +7377,21 @@
}
},
"node_modules/node-switchbot": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.0.3.tgz",
- "integrity": "sha512-fPmtTN/ba1fG6uW/6Witdju1i9C8WJgUYxR0hfeF09A0HZ6MnNLV01et3kVyCcXyUsdyjthp7qms/h4lFq0mmA==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/node-switchbot/-/node-switchbot-2.1.1.tgz",
+ "integrity": "sha512-QmjlroUt721Ik8Bq8AhqBqMMZgqXNF4jdbuGRpbLFOUcRXRmrecBVLlbhvXy++4FLMWaCzAuXMnpJ1qxv+IYDw==",
"optional": true,
"dependencies": {
- "@abandonware/noble": "^1.9.2-24"
+ "@abandonware/noble": "^1.9.2-25"
},
"optionalDependencies": {
- "@abandonware/bluetooth-hci-socket": "^0.5.3-11"
+ "@abandonware/bluetooth-hci-socket": "^0.5.3-12"
}
},
"node_modules/nodemon": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz",
- "integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.1.tgz",
+ "integrity": "sha512-k43xGaDtaDIcufn0Fc6fTtsdKSkV/hQzoQFigNH//GaKta28yoKVYXCnV+KXRqfT/YzsFaQU9VdeEG+HEyxr6A==",
"dev": true,
"dependencies": {
"chokidar": "^3.5.2",
@@ -7254,16 +7416,6 @@
"url": "https://opencollective.com/nodemon"
}
},
- "node_modules/nodemon/node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/nodemon/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -7273,18 +7425,6 @@
"node": ">=4"
}
},
- "node_modules/nodemon/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/nodemon/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -7358,9 +7498,9 @@
}
},
"node_modules/normalize-url": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz",
- "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==",
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz",
+ "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==",
"dev": true,
"engines": {
"node": ">=14.16"
@@ -7370,9 +7510,9 @@
}
},
"node_modules/npm-bundled": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.0.tgz",
- "integrity": "sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz",
+ "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==",
"dev": true,
"dependencies": {
"npm-normalize-package-bin": "^3.0.0"
@@ -7382,11 +7522,12 @@
}
},
"node_modules/npm-check-updates": {
- "version": "16.14.15",
- "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.15.tgz",
- "integrity": "sha512-WH0wJ9j6CP7Azl+LLCxWAYqroT2IX02kRIzgK/fg0rPpMbETgHITWBdOPtrv521xmA3JMgeNsQ62zvVtS/nCmQ==",
+ "version": "16.14.20",
+ "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.20.tgz",
+ "integrity": "sha512-sYbIhun4DrjO7NFOTdvs11nCar0etEhZTsEjL47eM0TuiGMhmYughRCxG2SpGRmGAQ7AkwN7bw2lWzoE7q6yOQ==",
"dev": true,
"dependencies": {
+ "@types/semver-utils": "^1.1.1",
"chalk": "^5.3.0",
"cli-table3": "^0.6.3",
"commander": "^10.0.1",
@@ -7452,6 +7593,15 @@
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
+ "node_modules/npm-check-updates/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
"node_modules/npm-check-updates/node_modules/cacache": {
"version": "17.1.4",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.4.tgz",
@@ -7496,28 +7646,6 @@
"node": ">=14"
}
},
- "node_modules/npm-check-updates/node_modules/glob": {
- "version": "10.3.10",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
- "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
- "dev": true,
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^2.3.5",
- "minimatch": "^9.0.1",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
- "path-scurry": "^1.10.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/npm-check-updates/node_modules/http-proxy-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
@@ -7589,6 +7717,21 @@
"node": ">=8"
}
},
+ "node_modules/npm-check-updates/node_modules/minimatch": {
+ "version": "9.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
+ "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/npm-check-updates/node_modules/minipass-collect": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
@@ -7792,34 +7935,12 @@
}
},
"node_modules/npm-registry-fetch/node_modules/cacache/node_modules/minipass": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
- "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
- "dev": true,
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/npm-registry-fetch/node_modules/glob": {
- "version": "10.3.10",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
- "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"dev": true,
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^2.3.5",
- "minimatch": "^9.0.1",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
- "path-scurry": "^1.10.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
"engines": {
"node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/npm-registry-fetch/node_modules/http-proxy-agent": {
@@ -7935,6 +8056,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
"integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
+ "deprecated": "This package is no longer supported.",
"optional": true,
"dependencies": {
"are-we-there-yet": "^2.0.0",
@@ -7979,12 +8101,12 @@
}
},
"node_modules/object-is": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
- "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
+ "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
"dependencies": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.1.3"
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
@@ -8056,24 +8178,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/openapi-types": {
- "version": "12.1.3",
- "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz",
- "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==",
- "dev": true
- },
"node_modules/optionator": {
- "version": "0.9.3",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
- "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
"dev": true,
"dependencies": {
- "@aashutoshrathi/word-wrap": "^1.2.3",
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
"levn": "^0.4.1",
"prelude-ls": "^1.2.1",
- "type-check": "^0.4.0"
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
},
"engines": {
"node": ">= 0.8.0"
@@ -8241,34 +8357,12 @@
}
},
"node_modules/pacote/node_modules/cacache/node_modules/minipass": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
- "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
- "dev": true,
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/pacote/node_modules/glob": {
- "version": "10.3.10",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
- "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
"dev": true,
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^2.3.5",
- "minimatch": "^9.0.1",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
- "path-scurry": "^1.10.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
"engines": {
"node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/pacote/node_modules/lru-cache": {
@@ -8401,25 +8495,25 @@
}
},
"node_modules/path-scurry": {
- "version": "1.10.1",
- "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz",
- "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==",
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
"devOptional": true,
"dependencies": {
- "lru-cache": "^9.1.1 || ^10.0.0",
+ "lru-cache": "^10.2.0",
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
},
"engines": {
- "node": ">=16 || 14 >=14.17"
+ "node": ">=16 || 14 >=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/path-scurry/node_modules/lru-cache": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
- "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
+ "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
"devOptional": true,
"engines": {
"node": "14 || >=16.14"
@@ -8456,43 +8550,43 @@
}
},
"node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"engines": {
- "node": ">=8.6"
+ "node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pino": {
- "version": "8.18.0",
- "resolved": "https://registry.npmjs.org/pino/-/pino-8.18.0.tgz",
- "integrity": "sha512-Mz/gKiRyuXu4HnpHgi1YWdHQCoWMufapzooisvFn78zl4dZciAxS+YeRkUxXl1ee/SzU80YCz1zpECCh4oC6Aw==",
+ "version": "8.21.0",
+ "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz",
+ "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==",
"dev": true,
"dependencies": {
"atomic-sleep": "^1.0.0",
"fast-redact": "^3.1.1",
"on-exit-leak-free": "^2.1.0",
- "pino-abstract-transport": "v1.1.0",
+ "pino-abstract-transport": "^1.2.0",
"pino-std-serializers": "^6.0.0",
"process-warning": "^3.0.0",
"quick-format-unescaped": "^4.0.3",
"real-require": "^0.2.0",
"safe-stable-stringify": "^2.3.1",
"sonic-boom": "^3.7.0",
- "thread-stream": "^2.0.0"
+ "thread-stream": "^2.6.0"
},
"bin": {
"pino": "bin.js"
}
},
"node_modules/pino-abstract-transport": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.1.0.tgz",
- "integrity": "sha512-lsleG3/2a/JIWUtf9Q5gUNErBqwIu1tUKTT3dUzaf5DySw9ra1wcqKjJjLX1VTY64Wk1eEOYsVGSaGfCK85ekA==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz",
+ "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==",
"dev": true,
"dependencies": {
"readable-stream": "^4.0.0",
@@ -8554,10 +8648,18 @@
"integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==",
"dev": true
},
+ "node_modules/possible-typed-array-names": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
+ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/prebuild-install": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz",
- "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz",
+ "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==",
"dev": true,
"dependencies": {
"detect-libc": "^2.0.0",
@@ -8743,11 +8845,11 @@
}
},
"node_modules/qs": {
- "version": "6.11.2",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
- "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
+ "version": "6.12.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz",
+ "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==",
"dependencies": {
- "side-channel": "^1.0.4"
+ "side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
@@ -8840,6 +8942,7 @@
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz",
"integrity": "sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==",
+ "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.",
"dev": true,
"dependencies": {
"glob": "^10.2.2",
@@ -8864,28 +8967,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
- "node_modules/read-package-json/node_modules/glob": {
- "version": "10.3.10",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
- "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
- "dev": true,
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^2.3.5",
- "minimatch": "^9.0.1",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
- "path-scurry": "^1.10.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
@@ -8911,6 +8992,18 @@
"node": ">=8.10.0"
}
},
+ "node_modules/readdirp/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/real-require": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
@@ -8921,15 +9014,9 @@
}
},
"node_modules/reflect-metadata": {
- "version": "0.1.14",
- "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz",
- "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==",
- "dev": true
- },
- "node_modules/regenerator-runtime": {
- "version": "0.14.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
- "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
+ "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
"dev": true
},
"node_modules/regexp.prototype.flags": {
@@ -9042,13 +9129,19 @@
"node": ">=8"
}
},
+ "node_modules/restore-cursor/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
"node_modules/ret": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz",
- "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==",
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.4.3.tgz",
+ "integrity": "sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==",
"dev": true,
"engines": {
- "node": ">=4"
+ "node": ">=10"
}
},
"node_modules/retry": {
@@ -9076,9 +9169,9 @@
"integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg=="
},
"node_modules/rimraf": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz",
- "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==",
+ "version": "5.0.7",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz",
+ "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==",
"dev": true,
"dependencies": {
"glob": "^10.3.7"
@@ -9087,29 +9180,7 @@
"rimraf": "dist/esm/bin.mjs"
},
"engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/rimraf/node_modules/glob": {
- "version": "10.3.10",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
- "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
- "dev": true,
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^2.3.5",
- "minimatch": "^9.0.1",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
- "path-scurry": "^1.10.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
+ "node": ">=14.18"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -9166,12 +9237,12 @@
]
},
"node_modules/safe-regex2": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz",
- "integrity": "sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-3.1.0.tgz",
+ "integrity": "sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==",
"dev": true,
"dependencies": {
- "ret": "~0.2.0"
+ "ret": "~0.4.0"
}
},
"node_modules/safe-stable-stringify": {
@@ -9202,12 +9273,9 @@
"dev": true
},
"node_modules/semver": {
- "version": "7.6.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
- "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
+ "version": "7.6.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
+ "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
"bin": {
"semver": "bin/semver.js"
},
@@ -9249,29 +9317,30 @@
"dev": true
},
"node_modules/set-function-length": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz",
- "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==",
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"dependencies": {
- "define-data-property": "^1.1.2",
+ "define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.3",
+ "get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
- "has-property-descriptors": "^1.0.1"
+ "has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/set-function-name": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz",
- "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
"dependencies": {
- "define-data-property": "^1.0.1",
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
"functions-have-names": "^1.2.3",
- "has-property-descriptors": "^1.0.0"
+ "has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
@@ -9311,11 +9380,11 @@
}
},
"node_modules/side-channel": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz",
- "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
+ "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"dependencies": {
- "call-bind": "^1.0.6",
+ "call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
@@ -9328,10 +9397,16 @@
}
},
"node_modules/signal-exit": {
- "version": "3.0.7",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
- "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
- "devOptional": true
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
},
"node_modules/sigstore": {
"version": "1.9.0",
@@ -9387,28 +9462,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
- "node_modules/sigstore/node_modules/glob": {
- "version": "10.3.10",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
- "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
- "dev": true,
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^2.3.5",
- "minimatch": "^9.0.1",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
- "path-scurry": "^1.10.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/sigstore/node_modules/http-proxy-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
@@ -9601,9 +9654,9 @@
}
},
"node_modules/socket.io": {
- "version": "4.7.2",
- "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz",
- "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==",
+ "version": "4.7.5",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz",
+ "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==",
"dev": true,
"dependencies": {
"accepts": "~1.3.4",
@@ -9619,11 +9672,12 @@
}
},
"node_modules/socket.io-adapter": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz",
- "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==",
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz",
+ "integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==",
"dev": true,
"dependencies": {
+ "debug": "~4.3.4",
"ws": "~8.11.0"
}
},
@@ -9662,9 +9716,9 @@
}
},
"node_modules/socks": {
- "version": "2.7.3",
- "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.3.tgz",
- "integrity": "sha512-vfuYK48HXCTFD03G/1/zkIls3Ebr2YNa4qU9gHDZdblHLiqhJrJGkY3+0Nx0JpN9qBhJbVObc1CNciT1bIZJxw==",
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz",
+ "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==",
"devOptional": true,
"dependencies": {
"ip-address": "^9.0.5",
@@ -9676,12 +9730,12 @@
}
},
"node_modules/socks-proxy-agent": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz",
- "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==",
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz",
+ "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==",
"optional": true,
"dependencies": {
- "agent-base": "^7.0.2",
+ "agent-base": "^7.1.1",
"debug": "^4.3.4",
"socks": "^2.7.1"
},
@@ -9690,9 +9744,9 @@
}
},
"node_modules/sonic-boom": {
- "version": "3.8.0",
- "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.0.tgz",
- "integrity": "sha512-ybz6OYOUjoQQCQ/i4LU8kaToD8ACtYP+Cj5qd2AO36bwbdewxWJ3ArmJ2cr6AvxlL2o0PqnCcPGUgkILbfkaCA==",
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz",
+ "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==",
"dev": true,
"dependencies": {
"atomic-sleep": "^1.0.0"
@@ -9746,9 +9800,9 @@
}
},
"node_modules/spdx-exceptions": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.4.0.tgz",
- "integrity": "sha512-hcjppoJ68fhxA/cjbN4T8N6uCUejN8yFw69ttpqtBeCbF3u13n7mb31NB9jKwGTTWWnt9IbRA/mf1FprYS8wfw==",
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
+ "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
"dev": true
},
"node_modules/spdx-expression-parse": {
@@ -9762,9 +9816,9 @@
}
},
"node_modules/spdx-license-ids": {
- "version": "3.0.17",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz",
- "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==",
+ "version": "3.0.18",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz",
+ "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==",
"dev": true
},
"node_modules/split": {
@@ -9794,9 +9848,9 @@
"devOptional": true
},
"node_modules/ssri": {
- "version": "10.0.5",
- "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz",
- "integrity": "sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==",
+ "version": "10.0.6",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz",
+ "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==",
"devOptional": true,
"dependencies": {
"minipass": "^7.0.3"
@@ -9858,17 +9912,20 @@
}
},
"node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
"devOptional": true,
"dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
},
"engines": {
- "node": ">=8"
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/string-width-cjs": {
@@ -9886,6 +9943,39 @@
"node": ">=8"
}
},
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "devOptional": true
+ },
+ "node_modules/string-width/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "devOptional": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/string-width/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "devOptional": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
@@ -9927,6 +10017,7 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -9935,15 +10026,15 @@
}
},
"node_modules/swagger-ui-dist": {
- "version": "5.10.3",
- "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.10.3.tgz",
- "integrity": "sha512-fu3aozjxFWsmcO1vyt1q1Ji2kN7KlTd1vHy27E9WgPyXo9nrEzhQPqgxaAjbMsOmb8XFKNGo4Sa3Q+84Fh+pFw==",
+ "version": "5.11.2",
+ "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.2.tgz",
+ "integrity": "sha512-jQG0cRgJNMZ7aCoiFofnoojeSaa/+KgWaDlfgs8QN+BXoGMpxeMVY5OEnjq4OlNvF3yjftO8c9GRAgcHlO+u7A==",
"dev": true
},
"node_modules/systeminformation": {
- "version": "5.21.22",
- "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.21.22.tgz",
- "integrity": "sha512-gNHloAJSyS+sKWkwvmvozZ1eHrdVTEsynWMTY6lvLGBB70gflkBQFw8drXXr1oEXY84+Vr9tOOrN8xHZLJSycA==",
+ "version": "5.22.7",
+ "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.22.7.tgz",
+ "integrity": "sha512-AWxlP05KeHbpGdgvZkcudJpsmChc2Y5Eo/GvxG/iUA/Aws5LZKHAMSeAo+V+nD+nxWZaxrwpWcnx4SH3oxNL3A==",
"dev": true,
"os": [
"darwin",
@@ -9976,9 +10067,9 @@
}
},
"node_modules/tar": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
- "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==",
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
+ "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
"devOptional": true,
"dependencies": {
"chownr": "^2.0.0",
@@ -10098,12 +10189,6 @@
}
}
},
- "node_modules/text-decoding": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/text-decoding/-/text-decoding-1.0.0.tgz",
- "integrity": "sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==",
- "dev": true
- },
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@@ -10120,9 +10205,9 @@
}
},
"node_modules/thread-stream": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz",
- "integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==",
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz",
+ "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==",
"dev": true,
"dependencies": {
"real-require": "^0.2.0"
@@ -10170,32 +10255,14 @@
}
},
"node_modules/touch": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
- "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
+ "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
"dev": true,
- "dependencies": {
- "nopt": "~1.0.10"
- },
"bin": {
"nodetouch": "bin/nodetouch.js"
}
},
- "node_modules/touch/node_modules/nopt": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
- "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
- "dev": true,
- "dependencies": {
- "abbrev": "1"
- },
- "bin": {
- "nopt": "bin/nopt.js"
- },
- "engines": {
- "node": "*"
- }
- },
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
@@ -10211,9 +10278,9 @@
}
},
"node_modules/ts-api-utils": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz",
- "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==",
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+ "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
"dev": true,
"engines": {
"node": ">=16"
@@ -10319,28 +10386,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
- "node_modules/tuf-js/node_modules/glob": {
- "version": "10.3.10",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz",
- "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==",
- "dev": true,
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^2.3.5",
- "minimatch": "^9.0.1",
- "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0",
- "path-scurry": "^1.10.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/tuf-js/node_modules/http-proxy-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
@@ -10481,12 +10526,12 @@
}
},
"node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
"dev": true,
"engines": {
- "node": ">=10"
+ "node": ">=12.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -10507,9 +10552,9 @@
}
},
"node_modules/typescript": {
- "version": "5.3.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
- "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
+ "version": "5.4.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+ "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@@ -10519,6 +10564,29 @@
"node": ">=14.17"
}
},
+ "node_modules/typescript-eslint": {
+ "version": "8.0.0-alpha.16",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.0.0-alpha.16.tgz",
+ "integrity": "sha512-hseQjFKLOZXuBjGgEoYWKD+EL1yd2nVvqL9TLq8RELE1ZGkha15WS98GfwpREZkak+CuTPNsRHHNxeXUesQ/DA==",
+ "dev": true,
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.0.0-alpha.16",
+ "@typescript-eslint/parser": "8.0.0-alpha.16",
+ "@typescript-eslint/utils": "8.0.0-alpha.16"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
"node_modules/uid": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz",
@@ -10538,14 +10606,11 @@
"dev": true
},
"node_modules/undici": {
- "version": "6.6.2",
- "resolved": "https://registry.npmjs.org/undici/-/undici-6.6.2.tgz",
- "integrity": "sha512-vSqvUE5skSxQJ5sztTZ/CdeJb1Wq0Hf44hlYMciqHghvz+K88U0l7D6u1VsndoFgskDcnU+nG3gYmMzJVzd9Qg==",
- "dependencies": {
- "@fastify/busboy": "^2.0.0"
- },
+ "version": "6.18.1",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.18.1.tgz",
+ "integrity": "sha512-/0BWqR8rJNRysS5lqVmfc7eeOErcOP4tZpATVjJOojjHZ71gSYVAtFhEmadcIjwMIUehh5NFyKGsXCnXIajtbA==",
"engines": {
- "node": ">=18.0"
+ "node": ">=18.17"
}
},
"node_modules/undici-types": {
@@ -10554,14 +10619,6 @@
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
- "node_modules/undici/node_modules/@fastify/busboy": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz",
- "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==",
- "engines": {
- "node": ">=14"
- }
- },
"node_modules/unique-filename": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz",
@@ -10728,14 +10785,14 @@
"integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw=="
},
"node_modules/usb": {
- "version": "2.11.0",
- "resolved": "https://registry.npmjs.org/usb/-/usb-2.11.0.tgz",
- "integrity": "sha512-u5+NZ6DtoW8TIBtuSArQGAZZ/K15i3lYvZBAYmcgI+RcDS9G50/KPrUd3CrU8M92ahyCvg5e0gc8BDvr5Hwejg==",
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/usb/-/usb-2.13.0.tgz",
+ "integrity": "sha512-pTNKyxD1DfC1DYu8kFcIdpE8f33e0c2Sbmmi0HEs28HTVC555uocvYR1g5DDv4CBibacCh4BqRyYZJylN4mBbw==",
"hasInstallScript": true,
"optional": true,
"dependencies": {
"@types/w3c-web-usb": "^1.0.6",
- "node-addon-api": "^7.0.0",
+ "node-addon-api": "^8.0.0",
"node-gyp-build": "^4.5.0"
},
"engines": {
@@ -10743,12 +10800,12 @@
}
},
"node_modules/usb/node_modules/node-addon-api": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz",
- "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==",
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.0.0.tgz",
+ "integrity": "sha512-ipO7rsHEBqa9STO5C5T10fj732ml+5kLN1cAG8/jdHd56ldQeGj3Q7+scUS+VHK/qy1zLEwC4wMK5+yM0btPvw==",
"optional": true,
"engines": {
- "node": "^16 || ^18 || >= 20"
+ "node": "^18 || ^20 || >= 21"
}
},
"node_modules/util-deprecate": {
@@ -10794,21 +10851,18 @@
}
},
"node_modules/validate-npm-package-name": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz",
- "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz",
+ "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==",
"dev": true,
- "dependencies": {
- "builtins": "^5.0.0"
- },
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/validator": {
- "version": "13.11.0",
- "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz",
- "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==",
+ "version": "13.12.0",
+ "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz",
+ "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==",
"dev": true,
"engines": {
"node": ">= 0.10"
@@ -10877,29 +10931,32 @@
}
},
"node_modules/which-collection": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz",
- "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
"dependencies": {
- "is-map": "^2.0.1",
- "is-set": "^2.0.1",
- "is-weakmap": "^2.0.1",
- "is-weakset": "^2.0.1"
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/which-typed-array": {
- "version": "1.1.14",
- "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz",
- "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==",
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz",
+ "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==",
"dependencies": {
- "available-typed-arrays": "^1.0.6",
- "call-bind": "^1.0.5",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.7",
"for-each": "^0.3.3",
"gopd": "^1.0.1",
- "has-tostringtag": "^1.0.1"
+ "has-tostringtag": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
@@ -10917,48 +10974,33 @@
"string-width": "^1.0.2 || 2 || 3 || 4"
}
},
- "node_modules/widest-line": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
- "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==",
- "dev": true,
+ "node_modules/wide-align/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "devOptional": true
+ },
+ "node_modules/wide-align/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "devOptional": true,
"dependencies": {
- "string-width": "^5.0.1"
- },
- "engines": {
- "node": ">=12"
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/widest-line/node_modules/ansi-regex": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
- "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
- "dev": true,
"engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ "node": ">=8"
}
},
- "node_modules/widest-line/node_modules/emoji-regex": {
- "version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
- "dev": true
- },
- "node_modules/widest-line/node_modules/string-width": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
- "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "node_modules/widest-line": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
+ "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==",
"dev": true,
"dependencies": {
- "eastasianwidth": "^0.2.0",
- "emoji-regex": "^9.2.2",
- "strip-ansi": "^7.0.1"
+ "string-width": "^5.0.1"
},
"engines": {
"node": ">=12"
@@ -10967,19 +11009,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/widest-line/node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"dev": true,
- "dependencies": {
- "ansi-regex": "^6.0.1"
- },
"engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ "node": ">=0.10.0"
}
},
"node_modules/wrap-ansi": {
@@ -11017,6 +11053,26 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "devOptional": true
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "devOptional": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/wrap-ansi/node_modules/ansi-regex": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
@@ -11041,29 +11097,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/wrap-ansi/node_modules/emoji-regex": {
- "version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
- "devOptional": true
- },
- "node_modules/wrap-ansi/node_modules/string-width": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
- "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
- "devOptional": true,
- "dependencies": {
- "eastasianwidth": "^0.2.0",
- "emoji-regex": "^9.2.2",
- "strip-ansi": "^7.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/wrap-ansi/node_modules/strip-ansi": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
@@ -11096,6 +11129,12 @@
"typedarray-to-buffer": "^3.1.5"
}
},
+ "node_modules/write-file-atomic/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
"node_modules/ws": {
"version": "7.5.9",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
@@ -11129,9 +11168,9 @@
}
},
"node_modules/xml2js": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
- "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
+ "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
"dev": true,
"dependencies": {
"sax": ">=0.6.0",
@@ -11150,15 +11189,6 @@
"node": ">=4.0"
}
},
- "node_modules/xregexp": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.4.1.tgz",
- "integrity": "sha512-2u9HwfadaJaY9zHtRRnH6BY6CQVNQKkYm3oLtC9gJXXzfsbACg5X5e4EZZGVAH+YIfa+QA9lsFQTTe3HURF3ag==",
- "dev": true,
- "dependencies": {
- "@babel/runtime-corejs3": "^7.12.1"
- }
- },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
@@ -11172,15 +11202,6 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
- "node_modules/yaml": {
- "version": "2.3.4",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz",
- "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==",
- "dev": true,
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
diff --git a/package.json b/package.json
index f2cc11c5..a6665aad 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"displayName": "SwitchBot",
"name": "@switchbot/homebridge-switchbot",
- "version": "3.4.0",
+ "version": "3.5.0",
"description": "The SwitchBot plugin allows you to access your SwitchBot device(s) from HomeKit.",
"author": {
"name": "SwitchBot",
@@ -24,9 +24,10 @@
"bugs": {
"url": "https://github.com/OpenWonderLabs/homebridge-switchbot/issues"
},
+ "engineStrict": true,
"engines": {
- "homebridge": "^1.7.0",
- "node": "^18 || ^20"
+ "homebridge": "^1.8.2",
+ "node": "^18 || ^20 || ^22"
},
"main": "dist/index.js",
"scripts": {
@@ -76,27 +77,30 @@
"ir"
],
"dependencies": {
- "@homebridge/plugin-ui-utils": "^1.0.1",
+ "@homebridge/plugin-ui-utils": "^1.0.3",
"async-mqtt": "^2.6.3",
"fakegato-history": "^0.6.4",
- "homebridge-lib": "^6.7.3",
+ "homebridge-lib": "^7.0.1",
"rxjs": "^7.8.1",
- "undici": "^6.6.2"
+ "undici": "^6.18.1"
},
"optionalDependencies": {
- "node-switchbot": "2.0.3"
+ "node-switchbot": "2.1.1"
},
"devDependencies": {
- "@types/node": "^20.11.17",
- "@typescript-eslint/eslint-plugin": "^6.21.0",
- "@typescript-eslint/parser": "^6.21.0",
- "eslint": "^8.56.0",
- "homebridge": "^1.7.0",
- "homebridge-config-ui-x": "4.55.1",
- "nodemon": "^3.0.3",
- "npm-check-updates": "^16.14.15",
- "rimraf": "^5.0.5",
+ "@eslint/js": "^9.3.0",
+ "@stylistic/eslint-plugin": "^2.1.0",
+ "@types/eslint__js": "^8.42.3",
+ "@types/node": "^20.12.12",
+ "eslint": "^9.3.0",
+ "globals": "^15.3.0",
+ "homebridge": "^1.8.2",
+ "homebridge-config-ui-x": "4.56.2",
+ "nodemon": "^3.1.1",
+ "npm-check-updates": "^16.14.20",
+ "rimraf": "^5.0.7",
"ts-node": "^10.9.2",
- "typescript": "^5.3.3"
+ "typescript": "^5.4.5",
+ "typescript-eslint": "^8.0.0-alpha.14"
}
}
diff --git a/src/custom.d.ts b/src/custom.d.ts
index eea79da3..49926c56 100644
--- a/src/custom.d.ts
+++ b/src/custom.d.ts
@@ -1,6 +1,7 @@
-/* Copyright(C) 2017-2023, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+/* Copyright(C) 2017-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
*
* custom.d.ts: @switchbot/homebridge-switchbot platform class.
*/
declare module 'homebridge-lib'
+declare module 'homebridge-lib/EveHomeKitTypes'
declare module 'fakegato-history'
diff --git a/src/device/blindtilt.ts b/src/device/blindtilt.ts
index d9c74334..fbdbda3e 100644
--- a/src/device/blindtilt.ts
+++ b/src/device/blindtilt.ts
@@ -1,113 +1,63 @@
+/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+ *
+ * blindtilt.ts: @switchbot/homebridge-switchbot.
+ */
import { request } from 'undici';
-import { sleep } from '../utils.js';
-import { MqttClient } from 'mqtt';
+import { Devices } from '../settings.js';
import { interval, Subject } from 'rxjs';
-import asyncmqtt from 'async-mqtt';
-import { SwitchBotPlatform } from '../platform.js';
+import { deviceBase } from './device.js';
+import { BlindTiltMappingMode } from '../utils.js';
import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
-import { Service, PlatformAccessory, CharacteristicValue, Logging, API, HAP } from 'homebridge';
-import { device, devicesConfig, serviceData, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js';
-
-enum BlindTiltMappingMode {
- OnlyUp = 'only_up',
- OnlyDown = 'only_down',
- DownAndUp = 'down_and_up',
- UpAndDown = 'up_and_down',
- UseTiltForDirection = 'use_tilt_for_direction',
-}
-export class BlindTilt {
- public readonly api: API;
- public readonly log: Logging;
- public readonly config!: SwitchBotPlatformConfig;
- protected readonly hap: HAP;
+import type { SwitchBotPlatform } from '../platform.js';
+import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
+import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js';
+
+
+export class BlindTilt extends deviceBase {
// Services
- batteryService!: Service;
- lightSensorService?: Service;
- windowCoveringService!: Service;
-
- // Characteristic Values
- BatteryLevel!: CharacteristicValue;
- PositionState!: CharacteristicValue;
- TargetPosition!: CharacteristicValue;
- CurrentPosition!: CharacteristicValue;
- StatusLowBattery!: CharacteristicValue;
- FirmwareRevision!: CharacteristicValue;
- CurrentAmbientLightLevel?: CharacteristicValue;
- TargetHorizontalTiltAngle!: CharacteristicValue;
- CurrentHorizontalTiltAngle!: CharacteristicValue;
-
- // OpenAPI Status
- OpenAPI_InMotion: deviceStatus['moving'];
- OpenAPI_BatteryLevel: serviceData['battery'];
- OpenAPI_Direction: deviceStatus['direction'];
- OpenAPI_Calibration: serviceData['calibration'];
- OpenAPI_CurrentPosition: serviceData['position'];
- OpenAPI_FirmwareRevision: deviceStatus['version'];
- OpenAPI_CurrentAmbientLightLevel: deviceStatus['brightness'];
+ private WindowCovering: {
+ Name: CharacteristicValue;
+ Service: Service;
+ PositionState: CharacteristicValue;
+ TargetPosition: CharacteristicValue;
+ CurrentPosition: CharacteristicValue;
+ TargetHorizontalTiltAngle: CharacteristicValue;
+ CurrentHorizontalTiltAngle: CharacteristicValue;
+ };
+
+ private Battery: {
+ Name: CharacteristicValue;
+ Service: Service;
+ BatteryLevel: CharacteristicValue;
+ StatusLowBattery: CharacteristicValue;
+ ChargingState?: CharacteristicValue;
+ };
+
+ private LightSensor?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ CurrentAmbientLightLevel?: CharacteristicValue;
+ };
// OpenAPI Others
- Mode!: string;
- setPositionMode?: string | number;
mappingMode: BlindTiltMappingMode = BlindTiltMappingMode.OnlyUp;
- // BLE Status
- BLE_InMotion: serviceData['inMotion'];
- BLE_BatteryLevel: serviceData['battery'];
- BLE_Calibration: serviceData['calibration'];
- BLE_CurrentPosition: serviceData['position'];
- BLE_CurrentAmbientLightLevel: serviceData['lightLevel'];
-
- // BLE Others
- BLE_IsConnected?: boolean;
- spaceBetweenLevels!: number;
-
// Target
setNewTarget!: boolean;
setNewTargetTimer!: NodeJS.Timeout;
- //MQTT stuff
- mqttClient: MqttClient | null = null;
-
- // Config
- set_minStep!: number;
- updateRate!: number;
- set_minLux!: number;
- set_maxLux!: number;
- scanDuration!: number;
- deviceLogging!: string;
- deviceRefreshRate!: number;
- setCloseMode!: string;
- setOpenMode!: string;
-
// Updates
blindTiltUpdateInProgress!: boolean;
doBlindTiltUpdate!: Subject;
- // Connection
- private readonly OpenAPI: boolean;
- private readonly BLE: boolean;
-
constructor(
- private readonly platform: SwitchBotPlatform,
- private accessory: PlatformAccessory,
- public device: device & devicesConfig,
+ readonly platform: SwitchBotPlatform,
+ accessory: PlatformAccessory,
+ device: device & devicesConfig,
) {
- this.api = this.platform.api;
- this.log = this.platform.log;
- this.config = this.platform.config;
- this.hap = this.api.hap;
- // Connection
- this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI';
- this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI';
+ super(platform, accessory, device);
// default placeholders
- this.deviceLogs(device);
- this.refreshRate(device);
- this.scan(device);
- this.setupMqtt(device);
- this.deviceContext();
- this.deviceConfig(device);
-
this.mappingMode = (device.blindTilt?.mode as BlindTiltMappingMode) ?? BlindTiltMappingMode.OnlyUp;
this.debugLog(`Mapping mode: ${this.mappingMode}`);
@@ -116,57 +66,49 @@ export class BlindTilt {
this.blindTiltUpdateInProgress = false;
this.setNewTarget = false;
- // Retrieve initial values and updateHomekit
- this.refreshStatus();
-
- // set accessory information
- accessory
- .getService(this.hap.Service.AccessoryInformation)!
- .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
- .setCharacteristic(this.hap.Characteristic.Model, 'W2701600')
- .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId)
- .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
-
- // get the WindowCovering service if it exists, otherwise create a new WindowCovering service
- // you can create multiple services for each accessory
- const windowCoveringService = `${device.deviceName} ${device.deviceType}`;
- (this.windowCoveringService =
- accessory.getService(this.hap.Service.WindowCovering)
- || accessory.addService(this.hap.Service.WindowCovering)), windowCoveringService;
-
- this.windowCoveringService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.windowCoveringService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.windowCoveringService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
- }
-
- // create handlers for required characteristics
- this.windowCoveringService.setCharacteristic(this.hap.Characteristic.PositionState, this.PositionState);
-
- this.windowCoveringService
- .getCharacteristic(this.hap.Characteristic.CurrentPosition)
+ // Initialize WindowCovering Service
+ accessory.context.WindowCovering = accessory.context.WindowCovering ?? {};
+ this.WindowCovering = {
+ Name: accessory.context.WindowCovering.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service,
+ PositionState: accessory.context.PositionState ?? this.hap.Characteristic.PositionState.STOPPED,
+ TargetPosition: accessory.context.TargetPosition ?? 100,
+ CurrentPosition: accessory.context.CurrentPosition ?? 100,
+ TargetHorizontalTiltAngle: accessory.context.TargetHorizontalTiltAngle ?? 90,
+ CurrentHorizontalTiltAngle: accessory.context.CurrentHorizontalTiltAngle ?? 90,
+ };
+ accessory.context.WindowCovering = this.WindowCovering as object;
+
+ // Initialize WindowCovering Characteristics
+ this.WindowCovering.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.WindowCovering.Name)
+ .getCharacteristic(this.hap.Characteristic.TargetPosition)
.setProps({
- minStep: this.minStep(device),
+ minStep: device.blindTilt?.set_minStep ?? 1,
minValue: 0,
maxValue: 100,
validValueRanges: [0, 100],
})
.onGet(() => {
- return this.CurrentPosition;
- });
+ return this.WindowCovering.TargetPosition;
+ })
+ .onSet(this.TargetPositionSet.bind(this));
- this.windowCoveringService
- .getCharacteristic(this.hap.Characteristic.TargetPosition)
+ // Initialize WindowCovering CurrentPosition Characteristic
+ this.WindowCovering.Service
+ .getCharacteristic(this.hap.Characteristic.CurrentPosition)
.setProps({
- minStep: this.minStep(device),
+ minStep: device.blindTilt?.set_minStep ?? 1,
minValue: 0,
maxValue: 100,
validValueRanges: [0, 100],
- })
- .onSet(this.TargetPositionSet.bind(this));
+ }).onGet(() => {
+ return this.WindowCovering?.CurrentPosition ?? 0;
+ });
- this.CurrentHorizontalTiltAngle = 90;
- this.windowCoveringService
- .getCharacteristic(this.hap.Characteristic.CurrentHorizontalTiltAngle)
+ // Initialize WindowCovering TargetHorizontalTiltAngle Characteristic
+ this.WindowCovering.Service
+ .getCharacteristic(this.hap.Characteristic.TargetHorizontalTiltAngle)
.setProps({
minStep: 180,
minValue: -90,
@@ -174,31 +116,68 @@ export class BlindTilt {
validValues: [-90, 90],
})
.onGet(() => {
- // this.debugLog(`requested CurrentHorizontalTiltAngle: ${this.CurrentHorizontalTiltAngle}`);
- return this.CurrentHorizontalTiltAngle;
- });
+ return this.WindowCovering.TargetHorizontalTiltAngle;
+ })
+ .onSet(this.TargetHorizontalTiltAngleSet.bind(this));
- this.TargetHorizontalTiltAngle = 90;
- this.windowCoveringService
- .getCharacteristic(this.hap.Characteristic.TargetHorizontalTiltAngle)
+ // Initialize WindowCovering CurrentHorizontalTiltAngle Characteristic
+ this.WindowCovering.Service
+ .getCharacteristic(this.hap.Characteristic.CurrentHorizontalTiltAngle)
.setProps({
minStep: 180,
minValue: -90,
maxValue: 90,
validValues: [-90, 90],
- })
- .onSet(this.TargetHorizontalTiltAngleSet.bind(this));
-
- // Battery Service
- const batteryService = `${accessory.displayName} Battery`;
- (this.batteryService = this.accessory.getService(this.hap.Service.Battery)
- || accessory.addService(this.hap.Service.Battery)), batteryService;
+ }).onGet(() => {
+ return this.WindowCovering.CurrentHorizontalTiltAngle ?? 0;
+ });
- this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`);
- if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`);
+ // Initialize Battery Service
+ accessory.context.Battery = accessory.context.Battery ?? {};
+ this.Battery = {
+ Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`,
+ Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service,
+ BatteryLevel: accessory.context.BatteryLevel ?? 100,
+ StatusLowBattery: this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL,
+ ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING,
+ };
+ accessory.context.Battery = this.Battery as object;
+
+ // Initialize Battery Name Characteristic
+ 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 (this.LightSensor?.Service) {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`);
+ this.LightSensor.Service = accessory.getService(this.hap.Service.LightSensor) as Service;
+ accessory.removeService(this.LightSensor.Service);
+ accessory.context.LightSensor = {};
+ }
+ } else {
+ accessory.context.LightSensor = accessory.context.LightSensor ?? {};
+ this.LightSensor = {
+ Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`,
+ Service: accessory.getService(this.hap.Service.LightSensor) ?? accessory.addService(this.hap.Service.LightSensor) as Service,
+ CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001,
+ };
+ accessory.context.LightSensor = this.LightSensor as object;
+
+ // Initialize LightSensor Characteristics
+ this.LightSensor.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.LightSensor.Name)
+ .setCharacteristic(this.hap.Characteristic.StatusActive, true)
+ .getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel)
+ .onGet(() => {
+ return this.LightSensor?.CurrentAmbientLightLevel ?? 0.0001;
+ });
}
+ // Retrieve initial values and updateHomekit
+ this.refreshStatus();
+
// Update Homekit
this.updateHomeKitCharacteristics();
@@ -210,36 +189,16 @@ export class BlindTilt {
});
//regisiter webhook event handler
- if (this.device.webhook) {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`);
- this.platform.webhookEventHandler[this.device.deviceId] = async (context) => {
- try {
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Please Submit Logs: ` + 'https://tinyurl.com/SwitchBotBug');
- /*const { temperature, humidity } = context;
- const { CurrentTemperature, CurrentRelativeHumidity } = this;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` +
- '(temperature, humidity) = ' +
- `Webhook:(${temperature}, ${humidity}), ` +
- `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`);
- this.CurrentRelativeHumidity = humidity;
- this.CurrentTemperature = temperature;
- this.updateHomeKitCharacteristics();*/
- } catch (e: any) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
- + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
- }
- };
- }
+ this.registerWebhook(accessory, device);
// update slide progress
- interval(this.updateRate * 1000)
- //.pipe(skipWhile(() => this.blindTiltUpdateInProgress))
+ interval(this.deviceUpdateRate * 1000)
.subscribe(async () => {
- if (this.PositionState === this.hap.Characteristic.PositionState.STOPPED) {
+ if (this.WindowCovering.PositionState === this.hap.Characteristic.PositionState.STOPPED) {
return;
}
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Refresh Status When Moving, PositionState: ${this.PositionState}`);
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} Refresh Status When Moving,`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
await this.refreshStatus();
});
@@ -250,235 +209,241 @@ export class BlindTilt {
tap(() => {
this.blindTiltUpdateInProgress = true;
}),
- debounceTime(this.platform.config.options!.pushRate! * 1000),
+ debounceTime(this.devicePushRate * 1000),
)
.subscribe(async () => {
try {
await this.pushChanges();
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` +
- ` Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} failed pushChanges with ${device.connectionType} Connection,`
+ + ` Error Message: ${JSON.stringify(e.message)}`);
}
this.blindTiltUpdateInProgress = false;
});
}
/**
- * Parse the device status from the SwitchBot api
+ * Parse the device status from the SwitchBotBLE API
*/
- async parseStatus(): Promise {
- if (!this.device.enableCloudService && this.OpenAPI) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`);
- } else if (this.BLE) {
- await this.BLEparseStatus();
- } else if (this.OpenAPI && this.platform.config.credentials?.token) {
- await this.openAPIparseStatus();
- } else {
- await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`,
- );
- }
- }
-
- async BLEparseStatus(): Promise {
+ async BLEparseStatus(serviceData: serviceData): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
// CurrentPosition
- this.CurrentPosition = 100 - Number(this.BLE_CurrentPosition);
+ this.WindowCovering.CurrentPosition = 100 - Number(serviceData.position);
await this.setMinMax();
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition ${this.CurrentPosition}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition ${this.WindowCovering.CurrentPosition}`);
if (this.setNewTarget) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Checking Status ...`);
}
- if (this.setNewTarget && this.BLE_InMotion) {
+ if (this.setNewTarget && serviceData.inMotion) {
await this.setMinMax();
- if (Number(this.TargetPosition) > this.CurrentPosition) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition}`);
- this.PositionState = this.hap.Characteristic.PositionState.INCREASING;
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`);
- } else if (Number(this.TargetPosition) < this.CurrentPosition) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition}`);
- this.PositionState = this.hap.Characteristic.PositionState.DECREASING;
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`);
+ if (Number(this.WindowCovering.TargetPosition) > this.WindowCovering.CurrentPosition) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING;
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} INCREASING`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
+ } else if (Number(this.WindowCovering.TargetPosition) < this.WindowCovering.CurrentPosition) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING;
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} DECREASING`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
} else {
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} Standby2, CurrentPosition: ${this.CurrentPosition}`);
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby2,`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} STOPPED`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
}
} else {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`);
- this.TargetPosition = this.CurrentPosition;
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
+ this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition;
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`);
}
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` +
- ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},`
+ + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`);
if (!this.device.blindTilt?.hide_lightsensor) {
- this.set_minLux = this.minLux();
- this.set_maxLux = this.maxLux();
- this.spaceBetweenLevels = 9;
+ const set_minLux = this.device.blindTilt?.set_minLux ?? 1;
+ const set_maxLux = this.device.blindTilt?.set_maxLux ?? 6001;
+ const spaceBetweenLevels = 9;
+
+ if (this.LightSensor?.CurrentAmbientLightLevel === 0) {
+ this.LightSensor!.CurrentAmbientLightLevel = 0.0001;
+ }
// Brightness
- switch (this.BLE_CurrentAmbientLightLevel) {
+ switch (serviceData.lightLevel) {
case 1:
- this.CurrentAmbientLightLevel = this.set_minLux;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = set_minLux;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
break;
case 2:
- this.CurrentAmbientLightLevel = (this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels;
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` +
- ` Calculation: ${(this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels}`,
- );
+ this.LightSensor!.CurrentAmbientLightLevel = (set_maxLux - set_minLux) / spaceBetweenLevels;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},`
+ + ` Calculation: ${(set_maxLux - set_minLux) / spaceBetweenLevels}`);
break;
case 3:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 2;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 2;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
break;
case 4:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 3;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 3;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
break;
case 5:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 4;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 4;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
break;
case 6:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 5;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 5;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
break;
case 7:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 6;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 6;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
break;
case 8:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 7;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 7;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
break;
case 9:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 8;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 8;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
break;
case 10:
default:
- this.CurrentAmbientLightLevel = this.set_maxLux;
+ this.LightSensor!.CurrentAmbientLightLevel = set_maxLux;
this.debugLog();
}
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` +
- ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},`
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
}
// Battery
- this.BatteryLevel = Number(this.BLE_BatteryLevel);
- if (this.BatteryLevel < 10) {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
+ this.Battery.BatteryLevel = Number(serviceData.battery);
+ if (this.Battery.BatteryLevel < 10) {
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
} else {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
}
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` + ` StatusLowBattery: ${this.StatusLowBattery}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
}
- async openAPIparseStatus(): Promise {
+
+ /**
+ * Parse the device status from the SwitchBot OpenAPI
+ */
+ async openAPIparseStatus(deviceStatus: deviceStatus): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
- const [homekitPosition, homekitTiltAngle] = this.mapDeviceValuesToHomekitValues(Number(this.OpenAPI_CurrentPosition),
- String(this.OpenAPI_Direction));
- this.debugLog(` device: ${this.OpenAPI_CurrentPosition} => HK: ${homekitPosition}`);
+ const [homekitPosition, homekitTiltAngle] = this.mapDeviceValuesToHomekitValues(Number(deviceStatus.body.slidePosition),
+ String(deviceStatus.body.direction));
+ this.debugLog(` device: ${deviceStatus.body.slidePosition} => HK: ${homekitPosition}`);
- this.CurrentPosition = homekitPosition;
+ this.WindowCovering!.CurrentPosition = homekitPosition;
// CurrentPosition
await this.setMinMax();
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
if (homekitTiltAngle) {
- this.CurrentHorizontalTiltAngle = homekitTiltAngle!;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentHorizontalTiltAngle: ${this.CurrentHorizontalTiltAngle}`);
+ this.WindowCovering.CurrentHorizontalTiltAngle = homekitTiltAngle!;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentHorizontalTiltAngle: ${this.WindowCovering.CurrentHorizontalTiltAngle}`);
}
if (this.setNewTarget) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Checking Status ...`);
}
- if (this.setNewTarget && this.OpenAPI_InMotion) {
+ if (this.setNewTarget && deviceStatus.body.moving) {
await this.setMinMax();
- if (this.TargetPosition > this.CurrentPosition || (homekitTiltAngle && this.TargetHorizontalTiltAngle !== this.CurrentHorizontalTiltAngle)) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition} `);
- this.PositionState = this.hap.Characteristic.PositionState.INCREASING;
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`);
- } else if (this.TargetPosition < this.CurrentPosition) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition} `);
- this.PositionState = this.hap.Characteristic.PositionState.DECREASING;
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`);
+ if (this.WindowCovering.TargetPosition > this.WindowCovering.CurrentPosition
+ || (homekitTiltAngle && this.WindowCovering.TargetHorizontalTiltAngle !== this.WindowCovering.CurrentHorizontalTiltAngle)) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition} `);
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING;
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} INCREASING`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
+ } else if (this.WindowCovering.TargetPosition < this.WindowCovering.CurrentPosition) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.WindowCovering.CurrentPosition} `);
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING;
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} DECREASING`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.CurrentPosition} Standby because reached position,` + ` CurrentPosition: ${this.CurrentPosition}`,
- );
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby because reached position,`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} STOPPED`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Standby because device not moving,` + ` CurrentPosition: ${this.CurrentPosition}`,
- );
- this.TargetPosition = this.CurrentPosition;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby because device not moving,`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
+ this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition;
if (homekitTiltAngle) {
- this.TargetHorizontalTiltAngle = this.CurrentHorizontalTiltAngle;
+ this.WindowCovering.TargetHorizontalTiltAngle = this.WindowCovering.CurrentHorizontalTiltAngle;
}
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`);
}
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` +
- ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`,
- );
-
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},`
+ + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`);
if (!this.device.blindTilt?.hide_lightsensor) {
- this.set_minLux = this.minLux();
- this.set_maxLux = this.maxLux();
+ const set_minLux = this.device.blindTilt?.set_minLux ?? 1;
+ const set_maxLux = this.device.blindTilt?.set_maxLux ?? 6001;
// Brightness
- switch (this.OpenAPI_CurrentAmbientLightLevel) {
+ switch (deviceStatus.body.brightness) {
case 'dim':
- this.CurrentAmbientLightLevel = this.set_minLux;
+ this.LightSensor!.CurrentAmbientLightLevel = set_minLux;
break;
case 'bright':
default:
- this.CurrentAmbientLightLevel = this.set_maxLux;
+ this.LightSensor!.CurrentAmbientLightLevel = set_maxLux;
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
}
// BatteryLevel
- this.BatteryLevel = Number(this.OpenAPI_BatteryLevel);
- if (this.BatteryLevel < 10) {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
+ this.Battery.BatteryLevel = Number(deviceStatus.body.battery);
+ if (this.Battery.BatteryLevel < 10) {
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
} else {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
- }
- if (Number.isNaN(this.BatteryLevel)) {
- this.BatteryLevel = 100;
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
+ }
+ if (Number.isNaN(this.Battery.BatteryLevel)) {
+ this.Battery.BatteryLevel = 100;
+ }
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
+
+ // Firmware Version
+ const version = deviceStatus.body.version?.toString();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
+ let deviceVersion: string;
+ if (version?.includes('.') === false) {
+ const replace = version?.replace(/^V|-.*$/g, '');
+ const match = replace?.match(/.{1,1}/g);
+ const blindTiltVersion = match?.join('.') ?? '0.0.0';
+ deviceVersion = blindTiltVersion;
+ } else {
+ deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},`
- + ` StatusLowBattery: ${this.StatusLowBattery}`);
-
- // FirmwareRevision
- this.FirmwareRevision = this.OpenAPI_FirmwareRevision!;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
+ this.accessory
+ .getService(this.hap.Service.AccessoryInformation)!
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
+ .updateValue(deviceVersion);
+ this.accessory.context.deviceVersion = deviceVersion;
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
}
async refreshStatus(): Promise {
@@ -490,10 +455,8 @@ export class BlindTilt {
await this.openAPIRefreshStatus();
} else {
await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` +
- ` ${this.device.connectionType}, refreshStatus will not happen.`,
- );
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
}
}
@@ -510,21 +473,13 @@ export class BlindTilt {
// Start to monitor advertisement packets
(async () => {
// Start to monitor advertisement packets
- await switchbot.startScan({
- model: 'x',
- id: this.device.bleMac,
- });
+ await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac });
// Set an event handler
switchbot.onadvertisement = (ad: any) => {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
- if (this.device.bleMac === ad.address && ad.model === 'x') {
+ if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.BLE_Calibration = ad.serviceData.calibration;
- this.BLE_BatteryLevel = ad.serviceData.battery;
- this.BLE_InMotion = ad.serviceData.inMotion;
- this.BLE_CurrentPosition = ad.serviceData.position;
- this.BLE_CurrentAmbientLightLevel = ad.serviceData.lightLevel;
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
}
@@ -534,83 +489,27 @@ export class BlindTilt {
// Stop to monitor
await switchbot.stopScan();
// Update HomeKit
- await this.BLEparseStatus();
+ await this.BLEparseStatus(switchbot.onadvertisement.serviceData);
await this.updateHomeKitCharacteristics();
})();
- /*if (switchbot !== false) {
- switchbot
- .startScan({
- model: 'c',
- id: this.device.bleMac,
- })
- .then(async () => {
- // Set an event handler
- switchbot.onadvertisement = async (ad: ad) => {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` +
- ` BLE Address Found: ${ad.address}`,
- );
- this.BLE_Calibration = ad.serviceData.calibration;
- this.BLE_BatteryLevel = ad.serviceData.battery;
- this.BLE_InMotion = ad.serviceData.inMotion;
- this.BLE_CurrentPosition = ad.serviceData.position;
- this.BLE_CurrentAmbientLightLevel = ad.serviceData.lightLevel;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} calibration: ${ad.serviceData.calibration}, ` +
- `position: ${ad.serviceData.position}, lightLevel: ${ad.serviceData.lightLevel}, battery: ${ad.serviceData.battery}, ` +
- `inMotion: ${ad.serviceData.inMotion}`,
- );
-
- if (ad.serviceData) {
- this.BLE_IsConnected = true;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
- await this.stopScanning(switchbot);
- } else {
- this.BLE_IsConnected = false;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
- }
- };
- // Wait
- return await sleep(this.scanDuration * 1000);
- })
- .then(async () => {
- // Stop to monitor
- await this.stopScanning(switchbot);
- })
- .catch(async (e: any) => {
- this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
- await this.BLERefreshConnection(switchbot);
- });
- } else {
+ if (switchbot === undefined) {
await this.BLERefreshConnection(switchbot);
- }*/
+ }
}
async openAPIRefreshStatus(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
try {
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, {
- headers: this.platform.generateHeaders(),
- });
+ const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries,
+ `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() });
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
const deviceStatus: any = await body.json();
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
- this.OpenAPI_CurrentPosition = deviceStatus.body.slidePosition;
- this.OpenAPI_Direction = deviceStatus.body.direction;
- this.OpenAPI_InMotion = deviceStatus.body.moving;
- this.OpenAPI_CurrentAmbientLightLevel = deviceStatus.body.brightness;
- this.OpenAPI_BatteryLevel = deviceStatus.body.battery;
- this.OpenAPI_FirmwareRevision = deviceStatus.body.version;
- this.openAPIparseStatus();
+ this.openAPIparseStatus(deviceStatus);
this.updateHomeKitCharacteristics();
} else {
this.statusCode(statusCode);
@@ -618,10 +517,35 @@ export class BlindTilt {
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
+ }
+ }
+
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
+ if (device.webhook) {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`);
+ this.platform.webhookEventHandler[this.device.deviceId] = async (context) => {
+ try {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
+ const { slidePosition, battery, lightLevel } = context;
+ const { CurrentPosition } = this.WindowCovering;
+ const { BatteryLevel } = this.Battery;
+ const { CurrentAmbientLightLevel } = this.LightSensor ?? {};
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} (slidePosition, battery, lightLevel) = Webhook:(${slidePosition}, ${battery},`
+ + ` ${lightLevel}), current:(${CurrentPosition}, ${BatteryLevel}, ${CurrentAmbientLightLevel})`);
+ this.WindowCovering.CurrentPosition = slidePosition;
+ this.Battery.BatteryLevel = battery;
+ if (!device.blindTilt?.hide_lightsensor) {
+ this.LightSensor!.CurrentAmbientLightLevel = lightLevel;
+ }
+ this.updateHomeKitCharacteristics();
+ } catch (e: any) {
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
+ }
+ };
+ } else {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`);
}
}
@@ -634,9 +558,8 @@ export class BlindTilt {
await this.openAPIpushChanges();
} else {
await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`,
- );
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, pushChanges will not happen.`);
}
// Refresh the status from the API
interval(15000)
@@ -649,7 +572,7 @@ export class BlindTilt {
async BLEpushChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`);
- if (this.TargetPosition !== this.CurrentPosition) {
+ if (this.WindowCovering.TargetPosition !== this.WindowCovering.CurrentPosition) {
const switchbot = await this.platform.connectBLE();
// Convert to BLE Address
this.device.bleMac = this.device
@@ -657,33 +580,29 @@ export class BlindTilt {
.join(':')
.toLowerCase();
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
- this.SilentPerformance();
- const adjustedMode = this.setPositionMode || null;
- if (adjustedMode === null) {
- this.Mode = 'Default Mode';
- }
- this.debugLog(`${this.accessory.displayName} Mode: ${this.Mode}`);
+ const { setPositionMode, Mode }: { setPositionMode: number; Mode: string; } = await this.setPerformance();
+ this.debugLog(`${this.accessory.displayName} Mode: ${Mode}`);
if (switchbot !== false) {
switchbot
.discover({ model: 'c', quick: true, id: this.device.bleMac })
.then(async (device_list: any) => {
- this.infoLog(`${this.accessory.displayName} Target Position: ${this.TargetPosition}`);
- return await this.retry({
- max: this.maxRetry(),
+ this.infoLog(`${this.accessory.displayName} Target Position: ${this.WindowCovering.TargetPosition}`);
+ return await this.retryBLE({
+ max: await this.maxRetryBLE(),
fn: async () => {
- return await device_list[0].runToPos(100 - Number(this.TargetPosition), adjustedMode);
+ return await device_list[0].runToPos(100 - Number(this.WindowCovering.TargetPosition), setPositionMode);
},
});
})
.then(() => {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `Target Position: ${this.WindowCovering.TargetPosition} sent over BLE, sent successfully`);
})
.catch(async (e: any) => {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
await this.BLEPushConnection();
});
} else {
@@ -691,50 +610,35 @@ export class BlindTilt {
await this.BLEPushConnection();
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.` +
- ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`,
- );
- }
- }
-
- async retry({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise {
- return fn().catch(async (e: any) => {
- if (max === 0) {
- throw e;
- }
- this.infoLog(e);
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`);
- await sleep(1000);
- return this.retry({ max: max - 1, fn });
- });
- }
-
- maxRetry(): number {
- if (this.device.maxRetry) {
- return this.device.maxRetry;
- } else {
- return 5;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`);
}
}
async openAPIpushChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`);
const hasDifferentAndRelevantHorizontalTiltAngle =
- this.mappingMode === BlindTiltMappingMode.UseTiltForDirection && this.TargetHorizontalTiltAngle !== this.CurrentHorizontalTiltAngle;
- if (this.TargetPosition !== this.CurrentPosition || hasDifferentAndRelevantHorizontalTiltAngle || this.device.disableCaching) {
- const [direction, position] = this.mapHomekitValuesToDeviceValues(Number(this.TargetPosition), Number(this.TargetHorizontalTiltAngle));
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing ${this.TargetPosition} (device = ${direction};${position})`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Mode: ${this.Mode}`);
+ this.mappingMode === BlindTiltMappingMode.UseTiltForDirection
+ && this.WindowCovering.TargetHorizontalTiltAngle !== this.WindowCovering.CurrentHorizontalTiltAngle;
+ if (this.WindowCovering.TargetPosition !== this.WindowCovering.CurrentPosition
+ || hasDifferentAndRelevantHorizontalTiltAngle || this.device.disableCaching) {
+ const [direction, position] = this.mapHomekitValuesToDeviceValues(Number(this.WindowCovering.TargetPosition),
+ Number(this.WindowCovering.TargetHorizontalTiltAngle));
+ const { Mode }: { setPositionMode: number; Mode: string; } = await this.setPerformance();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` Pushing ${this.WindowCovering.TargetPosition} (device = ${direction};${position})`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Mode: ${Mode}`);
let bodyChange = '';
if (position === 100) {
bodyChange = JSON.stringify({
command: 'fullyOpen',
+ parameter: 'default',
commandType: 'command',
});
} else if (position === 0) {
bodyChange = JSON.stringify({
command: direction === 'up' ? 'closeUp' : 'closeDown',
+ parameter: 'default',
commandType: 'command',
});
} else {
@@ -757,25 +661,24 @@ export class BlindTilt {
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
} else {
this.statusCode(statusCode);
this.statusCode(deviceStatus.statusCode);
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.` +
- ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}` +
- ` CurrentHorizontalTiltAngle: ${this.CurrentHorizontalTiltAngle}, TargetPosition ${this.TargetHorizontalTiltAngle}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`
+ + ` CurrentHorizontalTiltAngle: ${this.WindowCovering.CurrentHorizontalTiltAngle},`
+ + ` TargetPosition ${this.WindowCovering.TargetHorizontalTiltAngle}`);
}
}
@@ -783,16 +686,16 @@ export class BlindTilt {
* Handle requests to set the value of the "Target Horizontal Tilt" characteristic
*/
async TargetHorizontalTiltAngleSet(value: CharacteristicValue): Promise {
- if (this.TargetHorizontalTiltAngle === this.accessory.context.TargetHorizontalTiltAngle) {
+ if (this.WindowCovering.TargetHorizontalTiltAngle === this.accessory.context.TargetHorizontalTiltAngle) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set TargetHorizontalTiltAngle: ${value}`);
} else {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetHorizontalTiltAngle: ${value}`);
}
//value = value < 0 ? -90 : 90;
- this.TargetHorizontalTiltAngle = value;
+ this.WindowCovering.TargetHorizontalTiltAngle = value;
if (this.device.mqttURL) {
- this.mqttPublish('TargetHorizontalTiltAngle', this.TargetHorizontalTiltAngle);
+ this.mqttPublish('TargetHorizontalTiltAngle', this.WindowCovering.TargetHorizontalTiltAngle.toString());
}
this.startUpdatingBlindTiltIfNeeded();
@@ -802,15 +705,15 @@ export class BlindTilt {
* Handle requests to set the value of the "Target Position" characteristic
*/
async TargetPositionSet(value: CharacteristicValue): Promise {
- if (this.TargetPosition === this.accessory.context.TargetPosition) {
+ if (this.WindowCovering.TargetPosition === this.accessory.context.TargetPosition) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set TargetPosition: ${value}`);
} else {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`);
}
- this.TargetPosition = value;
+ this.WindowCovering.TargetPosition = value;
if (this.device.mqttURL) {
- this.mqttPublish('TargetPosition', this.TargetPosition);
+ this.mqttPublish('TargetPosition', this.WindowCovering.TargetPosition.toString());
}
this.startUpdatingBlindTiltIfNeeded();
}
@@ -818,39 +721,37 @@ export class BlindTilt {
async startUpdatingBlindTiltIfNeeded(): Promise {
await this.setMinMax();
this.debugLog('setMinMax');
- if (this.TargetPosition > this.CurrentPosition || this.TargetHorizontalTiltAngle !== this.CurrentHorizontalTiltAngle) {
- this.PositionState = this.hap.Characteristic.PositionState.INCREASING;
+ if (this.WindowCovering.TargetPosition > this.WindowCovering.CurrentPosition
+ || this.WindowCovering.TargetHorizontalTiltAngle !== this.WindowCovering.CurrentHorizontalTiltAngle) {
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING;
this.setNewTarget = true;
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} value: ${this.CurrentPosition},` + ` CurrentPosition: ${this.CurrentPosition}`,
- );
- } else if (this.TargetPosition < this.CurrentPosition) {
- this.PositionState = this.hap.Characteristic.PositionState.DECREASING;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${this.WindowCovering.CurrentPosition},`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
+ } else if (this.WindowCovering.TargetPosition < this.WindowCovering.CurrentPosition) {
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING;
this.setNewTarget = true;
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} value: ${this.CurrentPosition},` + ` CurrentPosition: ${this.CurrentPosition}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${this.WindowCovering.CurrentPosition},`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
} else {
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
this.setNewTarget = false;
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} value: ${this.CurrentPosition},` + ` CurrentPosition: ${this.CurrentPosition}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${this.WindowCovering.CurrentPosition},`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
}
- this.windowCoveringService.setCharacteristic(this.hap.Characteristic.PositionState, this.PositionState);
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
+ this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.WindowCovering.PositionState);
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
/**
* If Blind Tilt movement time is short, the moving flag from backend is always false.
* The minimum time depends on the network control latency.
*/
clearTimeout(this.setNewTargetTimer);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateRate: ${this.updateRate}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateRate: ${this.deviceUpdateRate}`);
if (this.setNewTarget) {
this.setNewTargetTimer = setTimeout(() => {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} setNewTarget ${this.setNewTarget} timeout`);
this.setNewTarget = false;
- }, this.updateRate * 1000);
+ }, this.deviceUpdateRate * 1000);
}
this.doBlindTiltUpdate.next();
}
@@ -859,152 +760,105 @@ export class BlindTilt {
await this.setMinMax();
// CurrentHorizontalTiltAngle
if (this.mappingMode === BlindTiltMappingMode.UseTiltForDirection) {
- if (this.CurrentHorizontalTiltAngle === undefined || Number.isNaN(this.CurrentHorizontalTiltAngle)) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentHorizontalTiltAngle: ${this.CurrentHorizontalTiltAngle}`);
+ if (this.WindowCovering.CurrentHorizontalTiltAngle === undefined || Number.isNaN(this.WindowCovering.CurrentHorizontalTiltAngle)) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentHorizontalTiltAngle: ${this.WindowCovering.CurrentHorizontalTiltAngle}`);
} else {
if (this.device.mqttURL) {
- this.mqttPublish('CurrentHorizontalTiltAngle', this.CurrentHorizontalTiltAngle);
+ this.mqttPublish('CurrentHorizontalTiltAngle', this.WindowCovering.CurrentHorizontalTiltAngle.toString());
}
- this.accessory.context.CurrentHorizontalTiltAngle = this.CurrentHorizontalTiltAngle;
- this.windowCoveringService.updateCharacteristic(
+ this.accessory.context.CurrentHorizontalTiltAngle = this.WindowCovering.CurrentHorizontalTiltAngle;
+ this.WindowCovering.Service.updateCharacteristic(
this.hap.Characteristic.CurrentHorizontalTiltAngle,
- Number(this.CurrentHorizontalTiltAngle),
- );
+ Number(this.WindowCovering.CurrentHorizontalTiltAngle));
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}
- updateCharacteristic CurrentHorizontalTiltAngle: ${this.CurrentHorizontalTiltAngle}`);
+ updateCharacteristic CurrentHorizontalTiltAngle: ${this.WindowCovering.CurrentHorizontalTiltAngle}`);
}
}
// CurrentPosition
- if (this.CurrentPosition === undefined || Number.isNaN(this.CurrentPosition)) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
+ if (this.WindowCovering.CurrentPosition === undefined || Number.isNaN(this.WindowCovering.CurrentPosition)) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
} else {
if (this.device.mqttURL) {
- this.mqttPublish('CurrentPosition', this.CurrentPosition);
+ this.mqttPublish('CurrentPosition', this.WindowCovering.CurrentPosition.toString());
}
- this.accessory.context.CurrentPosition = this.CurrentPosition;
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.CurrentPosition, Number(this.CurrentPosition));
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentPosition: ${this.CurrentPosition}`);
+ this.accessory.context.CurrentPosition = this.WindowCovering.CurrentPosition;
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, Number(this.WindowCovering.CurrentPosition));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` updateCharacteristic CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
}
// PositionState
- if (this.PositionState === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} PositionState: ${this.PositionState}`);
+ if (this.WindowCovering.PositionState === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} PositionState: ${this.WindowCovering.PositionState}`);
} else {
if (this.device.mqttURL) {
- this.mqttPublish('PositionState', this.PositionState);
+ this.mqttPublish('PositionState', this.WindowCovering.PositionState.toString());
}
- this.accessory.context.PositionState = this.PositionState;
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.PositionState, Number(this.PositionState));
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic PositionState: ${this.PositionState}`);
+ this.accessory.context.PositionState = this.WindowCovering.PositionState;
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, Number(this.WindowCovering.PositionState));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
}
// TargetPosition
- if (this.TargetPosition === undefined || Number.isNaN(this.TargetPosition)) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.TargetPosition}`);
+ if (this.WindowCovering.TargetPosition === undefined || Number.isNaN(this.WindowCovering.TargetPosition)) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.WindowCovering.TargetPosition}`);
} else {
if (this.device.mqttURL) {
- this.mqttPublish('TargetPosition', this.TargetPosition);
+ this.mqttPublish('TargetPosition', this.WindowCovering.TargetPosition.toString());
}
- this.accessory.context.TargetPosition = this.TargetPosition;
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.TargetPosition, Number(this.TargetPosition));
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: ${this.TargetPosition}`);
+ this.accessory.context.TargetPosition = this.WindowCovering.TargetPosition;
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, Number(this.WindowCovering.TargetPosition));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` TargetPosition: ${this.WindowCovering.TargetPosition}`);
}
// CurrentAmbientLightLevel
if (!this.device.blindTilt?.hide_lightsensor) {
- if (this.CurrentAmbientLightLevel === undefined || Number.isNaN(this.CurrentAmbientLightLevel)) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
+ if (this.LightSensor!.CurrentAmbientLightLevel === undefined || Number.isNaN(this.LightSensor!.CurrentAmbientLightLevel)) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
} else {
if (this.device.mqttURL) {
- this.mqttPublish('CurrentAmbientLightLevel', this.CurrentAmbientLightLevel);
+ this.mqttPublish('CurrentAmbientLightLevel', this.LightSensor!.CurrentAmbientLightLevel.toString());
}
- this.accessory.context.CurrentAmbientLightLevel = this.CurrentAmbientLightLevel;
- this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` +
- ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
- );
+ this.accessory.context.CurrentAmbientLightLevel = this.LightSensor!.CurrentAmbientLightLevel;
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
}
}
// BatteryLevel
- if (this.BatteryLevel === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
+ if (this.Battery.BatteryLevel === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`);
} else {
if (this.device.mqttURL) {
- this.mqttPublish('BatteryLevel', this.BatteryLevel);
+ this.mqttPublish('BatteryLevel', this.Battery.BatteryLevel.toString());
}
- this.accessory.context.BatteryLevel = this.BatteryLevel;
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
+ this.accessory.context.BatteryLevel = this.Battery.BatteryLevel;
+ this.Battery?.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`);
}
// StatusLowBattery
- if (this.StatusLowBattery === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
+ if (this.Battery.StatusLowBattery === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`);
} else {
if (this.device.mqttURL) {
- this.mqttPublish('StatusLowBattery', this.StatusLowBattery);
- }
- this.accessory.context.StatusLowBattery = this.StatusLowBattery;
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
- }
- }
-
- /*
- * Publish MQTT message for topics of
- * 'homebridge-switchbot/blindtilt/xx:xx:xx:xx:xx:xx'
- */
- mqttPublish(topic: string, message: any) {
- const mac = this.device.deviceId
- ?.toLowerCase()
- .match(/[\s\S]{1,2}/g)
- ?.join(':');
- const options = this.device.mqttPubOptions || {};
- this.mqttClient?.publish(`homebridge-switchbot/blindtilt/${mac}/${topic}`, `${message}`, options);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${topic}/${message} options:${JSON.stringify(options)}`);
- }
-
- /*
- * Setup MQTT hadler if URL is specified.
- */
- async setupMqtt(device: device & devicesConfig): Promise {
- if (device.mqttURL) {
- try {
- const { connectAsync } = asyncmqtt;
- this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`);
- this.mqttClient.on('error', (e: Error) => {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`);
- });
- } catch (e) {
- this.mqttClient = null;
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`);
+ this.mqttPublish('StatusLowBattery', this.Battery.StatusLowBattery.toString());
}
+ this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery;
+ this.Battery?.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
}
- }
-
- async stopScanning(switchbot: any) {
- switchbot.stopScan();
- if (this.BLE_IsConnected) {
- await this.BLEparseStatus();
- await this.updateHomeKitCharacteristics();
+ if (this.Battery.ChargingState === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ChargingState: ${this.Battery.ChargingState}`);
} else {
- await this.BLERefreshConnection(switchbot);
- }
- }
-
- async getCustomBLEAddress(switchbot: any) {
- if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
- (async () => {
- // Start to monitor advertisement packets
- await switchbot.startScan({
- model: 'c',
- });
- // Set an event handler
- switchbot.onadvertisement = (ad: any) => {
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
- };
- await sleep(10000);
- // Stop to monitor
- switchbot.stopScan();
- })();
+ if (this.device.mqttURL) {
+ this.mqttPublish('ChargingState', this.Battery.ChargingState.toString());
+ }
+ this.accessory.context.ChargingState = this.Battery.ChargingState;
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, this.Battery.ChargingState);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` ChargingState: ${this.Battery.ChargingState}`);
}
}
@@ -1024,218 +878,74 @@ export class BlindTilt {
}
}
- async SilentPerformance() {
- if (Number(this.TargetPosition) > 50) {
+ async setPerformance() {
+ let setPositionMode: number;
+ let Mode: string;
+ if (Number(this.WindowCovering.TargetPosition) > 50) {
if (this.device.blindTilt?.setOpenMode === '1') {
- this.setPositionMode = 1;
- this.Mode = 'Silent Mode';
+ setPositionMode = 1;
+ Mode = 'Silent Mode';
+ } else if (this.device.blindTilt?.setOpenMode === '0') {
+ setPositionMode = 0;
+ Mode = 'Performance Mode';
} else {
- this.setPositionMode = 0;
- this.Mode = 'Performance Mode';
+ setPositionMode = 0;
+ Mode = 'Default Mode';
}
} else {
if (this.device.blindTilt?.setCloseMode === '1') {
- this.setPositionMode = 1;
- this.Mode = 'Silent Mode';
+ setPositionMode = 1;
+ Mode = 'Silent Mode';
+ } else if (this.device.blindTilt?.setOpenMode === '0') {
+ setPositionMode = 0;
+ Mode = 'Performance Mode';
} else {
- this.setPositionMode = 0;
- this.Mode = 'Performance Mode';
+ setPositionMode = 0;
+ Mode = 'Default Mode';
}
}
+ return { setPositionMode, Mode };
}
async setMinMax(): Promise {
if (this.device.blindTilt?.set_min) {
- if (Number(this.CurrentPosition) <= this.device.blindTilt?.set_min) {
- this.CurrentPosition = 0;
+ if (Number(this.WindowCovering.CurrentPosition) <= this.device.blindTilt?.set_min) {
+ this.WindowCovering.CurrentPosition = 0;
}
}
if (this.device.blindTilt?.set_max) {
- if (Number(this.CurrentPosition) >= this.device.blindTilt?.set_max) {
- this.CurrentPosition = 100;
+ if (Number(this.WindowCovering.CurrentPosition) >= this.device.blindTilt?.set_max) {
+ this.WindowCovering.CurrentPosition = 100;
}
}
if (this.mappingMode === BlindTiltMappingMode.UseTiltForDirection) {
- this.CurrentHorizontalTiltAngle = Number(this.CurrentHorizontalTiltAngle) < 0 ? -90 : 90;
- }
- }
-
- minStep(device: device & devicesConfig): number {
- if (device.blindTilt?.set_minStep) {
- this.set_minStep = device.blindTilt?.set_minStep;
- } else {
- this.set_minStep = 1;
- }
- return this.set_minStep;
- }
-
- minLux(): number {
- if (this.device.blindTilt?.set_minLux) {
- this.set_minLux = this.device.blindTilt?.set_minLux;
- } else {
- this.set_minLux = 1;
- }
- return this.set_minLux;
- }
-
- maxLux(): number {
- if (this.device.blindTilt?.set_maxLux) {
- this.set_maxLux = this.device.blindTilt?.set_maxLux;
- } else {
- this.set_maxLux = 6001;
- }
- return this.set_maxLux;
- }
-
- async scan(device: device & devicesConfig): Promise {
- if (device.scanDuration) {
- if (this.updateRate > device.scanDuration) {
- this.scanDuration = this.updateRate;
- if (this.BLE) {
- this.warnLog(
- `${this.device.deviceType}: ` +
- `${this.accessory.displayName} scanDuration is less than updateRate, overriding scanDuration with updateRate`,
- );
- }
- } else {
- this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
- }
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
- }
- } else {
- if (this.updateRate > 1) {
- this.scanDuration = this.updateRate;
- } else {
- this.scanDuration = this.accessory.context.scanDuration = 1;
- }
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
- }
- }
- }
-
- async statusCode(statusCode: number): Promise {
- switch (statusCode) {
- case 151:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
- break;
- case 152:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
- break;
- case 160:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
- break;
- case 161:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
- this.offlineOff();
- break;
- case 171:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` +
- `Hub: ${this.device.hubDeviceId}`,
- );
- this.offlineOff();
- break;
- case 190:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
- ` Or command format is invalid, statusCode: ${statusCode}`,
- );
- break;
- case 100:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
- break;
- case 200:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
- break;
- case 400:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. `
- + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`);
- break;
- case 401:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, `
- + `but the request has not been authenticated, statusCode: ${statusCode}`);
- break;
- case 403:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not `
- + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`);
- break;
- case 404:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, `
- + `statusCode: ${statusCode}`);
- break;
- case 406:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via `
- + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`);
- break;
- case 415:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType `
- + `header that is not supported by the server, statusCode: ${statusCode}`);
- break;
- case 422:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, The client has made a valid request, `
- + `but the server cannot process it. This is often used for APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
- break;
- case 429:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of `
- + `requests allowed for a given time window, statusCode: ${statusCode}`);
- break;
- case 500:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings `
- + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`);
- break;
- default:
- this.infoLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` +
- `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`,
- );
+ this.WindowCovering.CurrentHorizontalTiltAngle = Number(this.WindowCovering.CurrentHorizontalTiltAngle) < 0 ? -90 : 90;
}
}
async offlineOff(): Promise {
if (this.device.offline) {
- await this.deviceContext();
- await this.updateHomeKitCharacteristics();
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100);
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED);
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100);
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentHorizontalTiltAngle, 90);
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetHorizontalTiltAngle, 90);
}
}
async apiError(e: any): Promise {
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e);
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.PositionState, e);
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.TargetPosition, e);
- if (!this.device.curtain?.hide_lightsensor) {
- this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e);
- }
- if (this.BLE) {
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e);
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e);
- }
- //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
- }
-
- async deviceContext() {
- if (this.CurrentPosition === undefined) {
- this.CurrentPosition = 0;
- } else {
- this.CurrentPosition = this.accessory.context.CurrentPosition;
- }
-
- if (this.TargetPosition === undefined) {
- this.TargetPosition = 0;
- } else {
- this.TargetPosition = this.accessory.context.TargetPosition;
- }
-
- if (this.PositionState === undefined) {
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
- } else {
- this.PositionState = this.accessory.context.PositionState;
- }
- if (this.FirmwareRevision === undefined) {
- this.FirmwareRevision = this.platform.version;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e);
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e);
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e);
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentHorizontalTiltAngle, e);
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetHorizontalTiltAngle, e);
+ 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.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e);
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e);
}
}
@@ -1358,130 +1068,4 @@ export class BlindTilt {
}
}
}
-
- async refreshRate(device: device & devicesConfig): Promise {
- // refreshRate
- if (device.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
- } else if (this.platform.config.options!.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
- }
- // updateRate
- if (device?.blindTilt?.updateRate) {
- this.updateRate = device?.blindTilt?.updateRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Blind Tilt updateRate: ${this.updateRate}`);
- } else {
- this.updateRate = 2;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default Blind Tilt updateRate: ${this.updateRate}`);
- }
- }
-
- async deviceConfig(device: device & devicesConfig): Promise {
- let config = {};
- if (device.blindTilt) {
- config = device.blindTilt;
- }
- if (device.connectionType !== undefined) {
- config['connectionType'] = device.connectionType;
- }
- if (device.external !== undefined) {
- config['external'] = device.external;
- }
- if (device.mqttURL !== undefined) {
- config['mqttURL'] = device.mqttURL;
- }
- if (device.logging !== undefined) {
- config['logging'] = device.logging;
- }
- if (device.refreshRate !== undefined) {
- config['refreshRate'] = device.refreshRate;
- }
- if (device.scanDuration !== undefined) {
- config['scanDuration'] = device.scanDuration;
- }
- if (device.maxRetry !== undefined) {
- config['maxRetry'] = device.maxRetry;
- }
- if (device.webhook !== undefined) {
- config['webhook'] = device.webhook;
- }
- if (device.blindTilt?.mode === undefined) {
- config['mode'] = BlindTiltMappingMode.OnlyUp;
- } else {
- config['mode'] = device.blindTilt?.mode;
- }
-
- if (Object.entries(config).length !== 0) {
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
- }
- }
-
- async deviceLogs(device: device & devicesConfig): Promise {
- if (this.platform.debugMode) {
- this.deviceLogging = this.accessory.context.logging = 'debugMode';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
- } else if (device.logging) {
- this.deviceLogging = this.accessory.context.logging = device.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
- } else if (this.platform.config.options?.logging) {
- this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
- } else {
- this.deviceLogging = this.accessory.context.logging = 'standard';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
- }
- }
-
- /**
- * Logging for Device
- */
- infoLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.info(String(...log));
- }
- }
-
- warnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.warn(String(...log));
- }
- }
-
- debugWarnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.warn('[DEBUG]', String(...log));
- }
- }
- }
-
- errorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.error(String(...log));
- }
- }
-
- debugErrorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.error('[DEBUG]', String(...log));
- }
- }
- }
-
- debugLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging === 'debug') {
- this.platform.log.info('[DEBUG]', String(...log));
- } else {
- this.platform.log.debug(String(...log));
- }
- }
- }
-
- enablingDeviceLogging(): boolean {
- return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard';
- }
}
diff --git a/src/device/bot.ts b/src/device/bot.ts
index 7b581601..f710d4b6 100644
--- a/src/device/bot.ts
+++ b/src/device/bot.ts
@@ -1,114 +1,155 @@
+/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+ *
+ * bot.ts: @switchbot/homebridge-switchbot.
+ */
import { request } from 'undici';
-import { sleep } from '../utils.js';
+import { deviceBase } from './device.js';
import { interval, Subject } from 'rxjs';
-import { SwitchBotPlatform } from '../platform.js';
+import { Devices } from '../settings.js';
import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
-import { Service, PlatformAccessory, CharacteristicValue, API, Logging, HAP } from 'homebridge';
-import { device, devicesConfig, deviceStatus, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js';
+
+import type { SwitchBotPlatform } from '../platform.js';
+import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
+import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js';
/**
* Platform Accessory
* An instance of this class is created for each accessory your platform registers
* Each accessory may expose multiple services of different service types.
*/
-export class Bot {
- public readonly api: API;
- public readonly log: Logging;
- public readonly config!: SwitchBotPlatformConfig;
- protected readonly hap: HAP;
+export class Bot extends deviceBase {
// Services
- fanService?: Service;
- doorService?: Service;
- lockService?: Service;
- faucetService?: Service;
- windowService?: Service;
- switchService?: Service;
- outletService?: Service;
- batteryService: Service;
- garageDoorService?: Service;
- windowCoveringService?: Service;
- statefulProgrammableSwitchService?: Service;
-
- // Characteristic Values
- On!: CharacteristicValue;
- BatteryLevel!: CharacteristicValue;
- FirmwareRevision!: CharacteristicValue;
- StatusLowBattery!: CharacteristicValue;
-
- // OpenAPI Status
- OpenAPI_On: deviceStatus['power'];
- OpenAPI_BatteryLevel: deviceStatus['battery'];
- OpenAPI_FirmwareRevision: deviceStatus['version'];
-
- // BLE Status
- BLE_On!: serviceData['state'];
- BLE_Mode!: serviceData['mode'];
- BLE_BatteryLevel!: serviceData['battery'];
-
- //BLE Others
- BLE_IsConnected?: boolean;
+ private Battery: {
+ Name: CharacteristicValue;
+ Service: Service;
+ BatteryLevel: CharacteristicValue;
+ StatusLowBattery: CharacteristicValue;
+ };
+
+ private Switch?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ On: CharacteristicValue;
+ };
+
+ private GarageDoor?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ On: CharacteristicValue;
+ };
+
+ private Door?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ On: CharacteristicValue;
+ };
+
+ private Window?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ On: CharacteristicValue;
+ };
+
+ private WindowCovering?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ On: CharacteristicValue;
+ };
+
+ private LockMechanism?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ On: CharacteristicValue;
+ };
+
+ private Faucet?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ On: CharacteristicValue;
+ };
+
+ private Fan?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ On: CharacteristicValue;
+ };
+
+ private StatefulProgrammableSwitch?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ On: CharacteristicValue;
+ };
+
+ private Outlet?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ On: CharacteristicValue;
+ };
// Config
botMode!: string;
allowPush?: boolean;
doublePress!: number;
- scanDuration!: number;
botDeviceType!: string;
pushRatePress!: number;
- deviceLogging!: string;
multiPressCount!: number;
- deviceRefreshRate!: number;
// Updates
botUpdateInProgress!: boolean;
doBotUpdate!: Subject;
- // Connection
- private readonly OpenAPI: boolean;
- private readonly BLE: boolean;
-
constructor(
- private readonly platform: SwitchBotPlatform,
- private accessory: PlatformAccessory,
- public device: device & devicesConfig,
+ readonly platform: SwitchBotPlatform,
+ accessory: PlatformAccessory,
+ device: device & devicesConfig,
) {
- this.api = this.platform.api;
- this.log = this.platform.log;
- this.config = this.platform.config;
- this.hap = this.api.hap;
- // Connection
- this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI';
- this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI';
+ super(platform, accessory, device);
// default placeholders
- this.deviceLogs(device);
- this.deviceType(device);
- this.scan(device);
- this.refreshRate(device);
- this.PressOrSwitch(device);
- this.allowPushChanges(device);
- this.deviceContext();
- this.DoublePress(device);
- this.deviceConfig(device);
-
- this.multiPressCount = 0;
+ this.getBotConfigSettings(device);
// this is subject we use to track when we need to POST changes to the SwitchBot API
this.doBotUpdate = new Subject();
this.botUpdateInProgress = false;
- // Retrieve initial values and updateHomekit
- this.refreshStatus();
-
- // set accessory information
- accessory
- .getService(this.hap.Service.AccessoryInformation)!
- .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
- .setCharacteristic(this.hap.Characteristic.Model, 'SWITCHBOT-BOT-S1')
- .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId)
- .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
+ accessory.context.Battery = accessory.context.Battery ?? {};
+ // Initialize Battery property
+ this.Battery = {
+ Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`,
+ Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service,
+ BatteryLevel: accessory.context.BatteryLevel ?? 100,
+ StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL,
+ };
+ accessory.context.Battery = this.Battery as object;
+
+ // 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') {
+ // Initialize Switch Service
+ accessory.context.Switch = accessory.context.Switch ?? {};
+ this.Switch = {
+ Name: accessory.context.Switch.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.Switch) ?? accessory.addService(this.hap.Service.Switch) as Service,
+ On: accessory.context.On ?? false,
+ };
+ accessory.context.Switch = this.Switch as object;
+ this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Switch`);
+
+ // Initialize Switch Characteristics
+ this.Switch.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.Switch.Name)
+ .setCharacteristic(this.hap.Characteristic.On, this.Switch.On)
+ .getCharacteristic(this.hap.Characteristic.On)
+ .onGet(() => {
+ return this.Switch!.On;
+ })
+ .onSet(this.OnSet.bind(this));
+
+ // Remove other services
this.removeFanService(accessory);
this.removeLockService(accessory);
this.removeDoorService(accessory);
@@ -118,19 +159,33 @@ export class Bot {
this.removeGarageDoorService(accessory);
this.removeWindowCoveringService(accessory);
this.removeStatefulProgrammableSwitchService(accessory);
+ } else if (this.botDeviceType === 'garagedoor') {
+ // Initialize GarageDoor Service
+ accessory.context.GarageDoor = accessory.context.GarageDoor ?? {};
+ this.GarageDoor = {
+ Name: accessory.context.GarageDoor.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.GarageDoorOpener) ?? accessory.addService(this.hap.Service.GarageDoorOpener) as Service,
+ On: accessory.context.On ?? false,
+ };
+ accessory.context.GarageDoor = this.GarageDoor as object;
+ this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Garage Door Opener`);
- // Add switchService
- const switchService = `${accessory.displayName} Switch`;
- (this.switchService = accessory.getService(this.hap.Service.Switch)
- || accessory.addService(this.hap.Service.Switch)), switchService;
- this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Switch`);
+ // 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,
+ })
+ .onGet(() => {
+ return this.GarageDoor!.On;
+ })
+ .onSet(this.OnSet.bind(this));
- this.switchService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.switchService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.switchService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
- }
- this.switchService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this));
- } else if (this.botDeviceType === 'garagedoor') {
+ // Remove other services
this.removeFanService(accessory);
this.removeLockService(accessory);
this.removeDoorService(accessory);
@@ -140,20 +195,33 @@ export class Bot {
this.removeWindowService(accessory);
this.removeWindowCoveringService(accessory);
this.removeStatefulProgrammableSwitchService(accessory);
+ } else if (this.botDeviceType === 'door') {
+ // Initialize Door Service
+ accessory.context.Door = accessory.context.Door ?? {};
+ this.Door = {
+ Name: accessory.context.Door.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.Door) ?? accessory.addService(this.hap.Service.Door) as Service,
+ On: accessory.context.On ?? false,
+ };
+ accessory.context.Door = this.Door as object;
+ this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Door`);
- // Add garageDoorService
- const garageDoorService = `${accessory.displayName} Garage Door Opener`;
- (this.garageDoorService = accessory.getService(this.hap.Service.GarageDoorOpener)
- || accessory.addService(this.hap.Service.GarageDoorOpener)), garageDoorService;
- this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Garage Door Opener`);
+ // 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,
+ })
+ .onGet(() => {
+ return this.Door!.On;
+ })
+ .onSet(this.OnSet.bind(this));
- this.garageDoorService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.garageDoorService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.garageDoorService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
- }
- this.garageDoorService.getCharacteristic(this.hap.Characteristic.TargetDoorState).onSet(this.OnSet.bind(this));
- this.garageDoorService.setCharacteristic(this.hap.Characteristic.ObstructionDetected, false);
- } else if (this.botDeviceType === 'door') {
+ // Remove other services
this.removeFanService(accessory);
this.removeLockService(accessory);
this.removeOutletService(accessory);
@@ -163,28 +231,33 @@ export class Bot {
this.removeGarageDoorService(accessory);
this.removeWindowCoveringService(accessory);
this.removeStatefulProgrammableSwitchService(accessory);
+ } else if (this.botDeviceType === 'window') {
+ // Initialize Window Service
+ accessory.context.Window = accessory.context.Window ?? {};
+ this.Window = {
+ Name: accessory.context.Window.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.Window) ?? accessory.addService(this.hap.Service.Window) as Service,
+ On: accessory.context.On ?? false,
+ };
+ accessory.context.Window = this.Window as object;
+ this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Window`);
- // Add doorService
- const doorService = `${accessory.displayName} Door`;
- (this.doorService = accessory.getService(this.hap.Service.Door)
- || accessory.addService(this.hap.Service.Door)), doorService;
- this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Door`);
-
- this.doorService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.doorService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.doorService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
- }
- this.doorService
- .getCharacteristic(this.hap.Characteristic.TargetPosition)
- .setProps({
+ // 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,
})
+ .onGet(() => {
+ return this.Window!.On;
+ })
.onSet(this.OnSet.bind(this));
- this.doorService.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED);
- } else if (this.botDeviceType === 'window') {
+
+ // Remove other services
this.removeFanService(accessory);
this.removeLockService(accessory);
this.removeDoorService(accessory);
@@ -194,28 +267,33 @@ export class Bot {
this.removeGarageDoorService(accessory);
this.removeWindowCoveringService(accessory);
this.removeStatefulProgrammableSwitchService(accessory);
+ } else if (this.botDeviceType === 'windowcovering') {
+ // Initialize WindowCovering Service
+ accessory.context.WindowCovering = accessory.context.WindowCovering ?? {};
+ this.WindowCovering = {
+ Name: accessory.context.WindowCovering.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service,
+ On: accessory.context.On ?? false,
+ };
+ accessory.context.WindowCovering = this.WindowCovering as object;
+ this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Window Covering`);
- // Add windowService
- const windowService = `${accessory.displayName} Window`;
- (this.windowService = accessory.getService(this.hap.Service.Window)
- || accessory.addService(this.hap.Service.Window)), windowService;
- this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Window`);
-
- this.windowService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.windowService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.windowService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
- }
- this.windowService
- .getCharacteristic(this.hap.Characteristic.TargetPosition)
- .setProps({
+ // 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,
})
+ .onGet(() => {
+ return this.WindowCovering!.On;
+ })
.onSet(this.OnSet.bind(this));
- this.windowService.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED);
- } else if (this.botDeviceType === 'windowcovering') {
+
+ // Remove other services
this.removeFanService(accessory);
this.removeLockService(accessory);
this.removeDoorService(accessory);
@@ -225,28 +303,27 @@ export class Bot {
this.removeWindowService(accessory);
this.removeGarageDoorService(accessory);
this.removeStatefulProgrammableSwitchService(accessory);
+ } else if (this.botDeviceType === 'lock') {
+ // Initialize Lock Service
+ accessory.context.LockMechanism = accessory.context.LockMechanism ?? {};
+ this.LockMechanism = {
+ Name: accessory.context.LockMechanism.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.LockMechanism) ?? accessory.addService(this.hap.Service.LockMechanism) as Service,
+ On: accessory.context.On ?? false,
+ };
+ accessory.context.LockMechanism = this.LockMechanism as object;
+ this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Lock`);
- // Add windowCoveringService
- const windowCoveringService = `${accessory.displayName} Window Covering`;
- (this.windowCoveringService = accessory.getService(this.hap.Service.WindowCovering)
- || accessory.addService(this.hap.Service.WindowCovering)), windowCoveringService;
- this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Window Covering`);
-
- this.windowCoveringService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.windowCoveringService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.windowCoveringService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
- }
- this.windowCoveringService
- .getCharacteristic(this.hap.Characteristic.TargetPosition)
- .setProps({
- validValues: [0, 100],
- minValue: 0,
- maxValue: 100,
- minStep: 100,
+ // Initialize Lock Characteristics
+ this.LockMechanism.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.LockMechanism.Name)
+ .getCharacteristic(this.hap.Characteristic.LockTargetState)
+ .onGet(() => {
+ return this.LockMechanism!.On;
})
.onSet(this.OnSet.bind(this));
- this.windowCoveringService.setCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED);
- } else if (this.botDeviceType === 'lock') {
+
+ // Remove other services
this.removeFanService(accessory);
this.removeDoorService(accessory);
this.removeOutletService(accessory);
@@ -256,19 +333,27 @@ export class Bot {
this.removeGarageDoorService(accessory);
this.removeWindowCoveringService(accessory);
this.removeStatefulProgrammableSwitchService(accessory);
+ } else if (this.botDeviceType === 'faucet') {
+ // Initialize Faucet Service
+ accessory.context.Faucet = accessory.context.Faucet ?? {};
+ this.Faucet = {
+ Name: accessory.context.Faucet.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.Faucet) ?? accessory.addService(this.hap.Service.Faucet) as Service,
+ On: accessory.context.On ?? false,
+ };
+ accessory.context.Faucet = this.Faucet as object;
+ this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Faucet`);
- // Add lockService
- const lockService = `${accessory.displayName} Lock`;
- (this.lockService = accessory.getService(this.hap.Service.LockMechanism)
- || accessory.addService(this.hap.Service.LockMechanism)), lockService;
- this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Lock`);
+ // Initialize Faucet Characteristics
+ this.Faucet.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.Faucet.Name)
+ .getCharacteristic(this.hap.Characteristic.Active)
+ .onGet(() => {
+ return this.Faucet!.On;
+ })
+ .onSet(this.OnSet.bind(this));
- this.lockService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.lockService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.lockService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
- }
- this.lockService.getCharacteristic(this.hap.Characteristic.LockTargetState).onSet(this.OnSet.bind(this));
- } else if (this.botDeviceType === 'faucet') {
+ // Remove other services
this.removeFanService(accessory);
this.removeLockService(accessory);
this.removeDoorService(accessory);
@@ -278,19 +363,27 @@ export class Bot {
this.removeGarageDoorService(accessory);
this.removeWindowCoveringService(accessory);
this.removeStatefulProgrammableSwitchService(accessory);
+ } else if (this.botDeviceType === 'fan') {
+ // Initialize Fan Service
+ accessory.context.Fan = accessory.context.Fan ?? {};
+ this.Fan = {
+ Name: accessory.context.Fan.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2) as Service,
+ On: accessory.context.On ?? false,
+ };
+ accessory.context.Fan = this.Fan as object;
+ this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Fan`);
- // Add faucetService
- const faucetService = `${accessory.displayName} Faucet`;
- (this.faucetService = accessory.getService(this.hap.Service.Faucet)
- || accessory.addService(this.hap.Service.Faucet)), faucetService;
- this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Faucet`);
+ // Initialize Fan Characteristics
+ this.Fan.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.Fan.Name)
+ .getCharacteristic(this.hap.Characteristic.On)
+ .onGet(() => {
+ return this.Fan!.On;
+ })
+ .onSet(this.OnSet.bind(this));
- this.faucetService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.faucetService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.faucetService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
- }
- this.faucetService.getCharacteristic(this.hap.Characteristic.Active).onSet(this.OnSet.bind(this));
- } else if (this.botDeviceType === 'fan') {
+ // Remove other services
this.removeLockService(accessory);
this.removeDoorService(accessory);
this.removeFaucetService(accessory);
@@ -300,19 +393,28 @@ export class Bot {
this.removeGarageDoorService(accessory);
this.removeWindowCoveringService(accessory);
this.removeStatefulProgrammableSwitchService(accessory);
+ } else if (this.botDeviceType === 'stateful') {
+ // Initialize StatefulProgrammableSwitch Service
+ accessory.context.StatefulProgrammableSwitch = accessory.context.StatefulProgrammableSwitch ?? {};
+ this.StatefulProgrammableSwitch = {
+ Name: accessory.context.StatefulProgrammableSwitch.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.StatefulProgrammableSwitch)
+ ?? accessory.addService(this.hap.Service.StatefulProgrammableSwitch) as Service,
+ On: accessory.context.On ?? false,
+ };
+ accessory.context.StatefulProgrammableSwitch = this.StatefulProgrammableSwitch as object;
+ this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Stateful Programmable Switch`);
- // Add fanService
- const fanService = `${accessory.displayName} Fan`;
- (this.fanService = accessory.getService(this.hap.Service.Fan)
- || accessory.addService(this.hap.Service.Fan)), fanService;
- this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Fan`);
+ // Initialize StatefulProgrammableSwitch Characteristics
+ this.StatefulProgrammableSwitch.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.StatefulProgrammableSwitch.Name)
+ .getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState)
+ .onGet(() => {
+ return this.StatefulProgrammableSwitch!.On;
+ })
+ .onSet(this.OnSet.bind(this));
- this.fanService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.fanService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.fanService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
- }
- this.fanService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this));
- } else if (this.botDeviceType === 'stateful') {
+ // Remove other services
this.removeFanService(accessory);
this.removeLockService(accessory);
this.removeDoorService(accessory);
@@ -322,21 +424,27 @@ export class Bot {
this.removeWindowService(accessory);
this.removeGarageDoorService(accessory);
this.removeWindowCoveringService(accessory);
+ } else {
+ // Initialize Switch property
+ accessory.context.Outlet = accessory.context.Outlet ?? {};
+ this.Outlet = {
+ Name: accessory.context.Outlet.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.Outlet) ?? accessory.addService(this.hap.Service.Outlet) as Service,
+ On: accessory.context.On ?? false,
+ };
+ accessory.context.Outlet = this.Outlet as object;
+ this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Outlet`);
- // Add statefulProgrammableSwitchService
- const statefulProgrammableSwitchService = `${accessory.displayName} Stateful Programmable Switch`;
- (this.statefulProgrammableSwitchService = accessory.getService(this.hap.Service.StatefulProgrammableSwitch) ||
- accessory.addService(this.hap.Service.StatefulProgrammableSwitch)), statefulProgrammableSwitchService;
- this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Stateful Programmable Switch`);
-
- this.statefulProgrammableSwitchService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.statefulProgrammableSwitchService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.statefulProgrammableSwitchService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
- }
- this.statefulProgrammableSwitchService
- .getCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState)
+ // Initialize Outlet Characteristics
+ this.Outlet.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.Outlet.Name)
+ .getCharacteristic(this.hap.Characteristic.On)
+ .onGet(() => {
+ return this.Outlet!.On;
+ })
.onSet(this.OnSet.bind(this));
- } else {
+
+ // Remove other services
this.removeFanService(accessory);
this.removeLockService(accessory);
this.removeDoorService(accessory);
@@ -346,30 +454,10 @@ export class Bot {
this.removeGarageDoorService(accessory);
this.removeWindowCoveringService(accessory);
this.removeStatefulProgrammableSwitchService(accessory);
-
- // Add outletService
- const outletService = `${accessory.displayName} Outlet`;
- (this.outletService = accessory.getService(this.hap.Service.Outlet)
- || accessory.addService(this.hap.Service.Outlet)), outletService;
- this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Displaying as Outlet`);
-
- this.outletService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.outletService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.outletService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
- }
- this.outletService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this));
}
- // batteryService
- const batteryService = `${accessory.displayName} Battery`;
- (this.batteryService = this.accessory.getService(this.hap.Service.Battery)
- || accessory.addService(this.hap.Service.Battery)), batteryService;
-
- this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`);
- if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`);
- }
- this.batteryService.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE);
+ // Retrieve initial values and updateHomekit
+ this.refreshStatus();
// Retrieve initial values and updateHomekit
this.updateHomeKitCharacteristics();
@@ -381,28 +469,8 @@ export class Bot {
await this.refreshStatus();
});
- //regisiter webhook event handler
- if (this.device.webhook) {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`);
- this.platform.webhookEventHandler[this.device.deviceId] = async (context) => {
- try {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
- const { power, battery, deviceMode } = context;
- const { On, BatteryLevel, botMode } = this;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` +
- '(power, battery, deviceMode) = ' +
- `Webhook:(${power}, ${battery}, ${deviceMode}), ` +
- `current:(${On}, ${BatteryLevel}, ${botMode})`);
- this.On = power;
- this.BatteryLevel = battery;
- this.botMode = deviceMode;
- this.updateHomeKitCharacteristics();
- } catch (e: any) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
- + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
- }
- };
- }
+ // regisiter webhook event handler
+ this.registerWebhook(accessory, device);
// Watch for Bot change events
// We put in a debounce of 1000ms so we don't make duplicate calls
@@ -411,7 +479,7 @@ export class Bot {
tap(() => {
this.botUpdateInProgress = true;
}),
- debounceTime(this.platform.config.options!.pushRate! * 1000),
+ debounceTime(this.devicePushRate * 1000),
)
.subscribe(async () => {
try {
@@ -426,90 +494,90 @@ export class Bot {
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` +
- ` Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} failed pushChanges with ${device.connectionType} Connection,`
+ + ` Error Message: ${JSON.stringify(e.message)}`);
}
this.botUpdateInProgress = false;
});
}
/**
- * Parse the device status from the SwitchBot api
+ * Parse the device status from the SwitchBotBLE API
*/
- async parseStatus(): Promise {
- if (!this.device.enableCloudService && this.OpenAPI) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`);
- } else if (this.BLE) {
- await this.BLEparseStatus();
- } else if (this.OpenAPI && this.platform.config.credentials?.token) {
- await this.openAPIparseStatus();
- } else {
- await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`,
- );
- }
- }
-
- async BLEparseStatus(): Promise {
+ async BLEparseStatus(serviceData: serviceData): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
// BLEmode (true if Switch Mode) | (false if Press Mode)
- if (this.BLE_Mode) {
- this.accessory.context.On = this.On;
- if (this.On === undefined) {
- this.On = Boolean(this.BLE_On);
+ if (serviceData.mode) {
+ this.accessory.context.On = await this.getOn();
+ if (this.getOn() === undefined) {
+ this.setOn(Boolean(serviceData.state));
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Switch Mode, mode: ${this.BLE_Mode}, On: ${this.On}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Switch Mode,`
+ + ` mode: ${serviceData.mode}, On: ${this.accessory.context.On}`);
} else {
- this.On = false;
- this.accessory.context.On = this.On;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Press Mode, mode: ${this.BLE_Mode}, On: ${this.On}`);
+ this.setOn(false);
+ this.accessory.context.On = await this.getOn();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Press Mode,`
+ + ` mode: ${serviceData.mode}, On: ${this.accessory.context.On}`);
}
- this.BatteryLevel = Number(this.BLE_BatteryLevel);
- if (this.BatteryLevel < 10) {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
+ this.Battery.BatteryLevel = Number(serviceData.battery);
+ if (this.Battery.BatteryLevel < 10) {
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
} else {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
}
- if (Number.isNaN(this.BatteryLevel)) {
- this.BatteryLevel = 100;
+ if (Number.isNaN(this.Battery.BatteryLevel)) {
+ this.Battery.BatteryLevel = 100;
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},`
- + ` StatusLowBattery: ${this.StatusLowBattery}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
}
- async openAPIparseStatus(): Promise {
+
+ /**
+ * Parse the device status from the SwitchBot OpenAPI
+ */
+ async openAPIparseStatus(deviceStatus: deviceStatus): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
if (this.botMode === 'press') {
- this.On = false;
- this.accessory.context.On = this.On;
+ this.setOn(false);
+ this.accessory.context.On = await this.getOn();
} else {
- this.accessory.context.On = this.On;
- if (this.On === undefined) {
- this.On = false;
+ this.accessory.context.On = await this.getOn();
+ if (this.getOn() === undefined) {
+ this.setOn(false);
}
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.accessory.context.On}`);
// Battery
- this.BatteryLevel = Number(this.OpenAPI_BatteryLevel);
- if (this.BatteryLevel < 10) {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
+ this.Battery.BatteryLevel = Number(deviceStatus.body.battery);
+ if (this.Battery.BatteryLevel < 10) {
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
} else {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
- }
- if (Number.isNaN(this.BatteryLevel)) {
- this.BatteryLevel = 100;
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
+ }
+ if (Number.isNaN(this.Battery.BatteryLevel)) {
+ this.Battery.BatteryLevel = 100;
+ }
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
+
+ // Firmware Version
+ const version = deviceStatus.body.version?.toString();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
+ if (deviceStatus.body.version) {
+ const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
+ this.accessory
+ .getService(this.hap.Service.AccessoryInformation)!
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
+ .updateValue(deviceVersion);
+ this.accessory.context.deviceVersion = deviceVersion;
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},`
- + ` StatusLowBattery: ${this.StatusLowBattery}`);
-
- // FirmwareRevision
- this.FirmwareRevision = this.OpenAPI_FirmwareRevision!;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
}
/**
@@ -524,10 +592,8 @@ export class Bot {
await this.openAPIRefreshStatus();
} else {
await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` +
- ` ${this.device.connectionType}, refreshStatus will not happen.`,
- );
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
}
}
@@ -544,99 +610,43 @@ export class Bot {
// Start to monitor advertisement packets
(async () => {
// Start to monitor advertisement packets
- await switchbot.startScan({
- model: 'H',
- id: this.device.bleMac,
- });
+ await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac });
// Set an event handler
switchbot.onadvertisement = (ad: any) => {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
- if (this.device.bleMac === ad.address && ad.model === 'H') {
+ if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.BLE_Mode = ad.serviceData.mode;
- this.BLE_On = ad.serviceData.state;
- this.BLE_BatteryLevel = ad.serviceData.battery;
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
}
};
- // Wait 1 seconds
+ // Wait 10 seconds
await switchbot.wait(this.scanDuration * 1000);
// Stop to monitor
await switchbot.stopScan();
// Update HomeKit
- await this.BLEparseStatus();
+ await this.BLEparseStatus(switchbot.onadvertisement.serviceData);
await this.updateHomeKitCharacteristics();
})();
- /*if (switchbot !== false) {
- switchbot
- .startScan({
- model: 'H',
- id: this.device.bleMac,
- })
- .then(async () => {
- // Set an event handler
- switchbot.onadvertisement = async (ad: ad) => {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` +
- ` BLE Address Found: ${ad.address}`,
- );
- this.BLE_Mode = ad.serviceData.mode;
- this.BLE_On = ad.serviceData.state;
- this.BLE_BatteryLevel = ad.serviceData.battery;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}, model: ${ad.serviceData.model}, modelName: ` +
- `${ad.serviceData.modelName}, mode: ${ad.serviceData.mode}, state: ${ad.serviceData.state}, battery: ${ad.serviceData.battery}`,
- );
-
- if (ad.serviceData) {
- this.BLE_IsConnected = true;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
- await this.stopScanning(switchbot);
- } else {
- this.BLE_IsConnected = false;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
- }
- };
- // Wait
- return await sleep(this.scanDuration * 1000);
- })
- .then(async () => {
- // Stop to monitor
- await this.stopScanning(switchbot);
- })
- .catch(async (e: any) => {
- this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
- await this.BLERefreshConnection(switchbot);
- });
- } else {
+ if (switchbot === undefined) {
await this.BLERefreshConnection(switchbot);
- }*/
+ }
}
async openAPIRefreshStatus(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
try {
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, {
- headers: this.platform.generateHeaders(),
- });
+ const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries,
+ `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() });
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
const deviceStatus: any = await body.json();
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
- this.OpenAPI_On = deviceStatus.body.power;
- this.OpenAPI_BatteryLevel = deviceStatus.body.battery;
- this.OpenAPI_FirmwareRevision = deviceStatus.body.version;
- this.openAPIparseStatus();
+ this.openAPIparseStatus(deviceStatus);
this.updateHomeKitCharacteristics();
} else {
this.statusCode(statusCode);
@@ -644,10 +654,33 @@ export class Bot {
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
+ }
+ }
+
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
+ if (device.webhook) {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`);
+ this.platform.webhookEventHandler[device.deviceId] = async (context) => {
+ try {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
+ const { power, battery, deviceMode } = context;
+ const { botMode } = this;
+ const On = await this.getOn();
+ const { BatteryLevel } = this.Battery;
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} (power, battery, deviceMode) = Webhook:(${power}, ${battery}, ${deviceMode}),`
+ + ` current:(${On}, ${BatteryLevel}, ${botMode})`);
+ await this.setOn(power);
+ this.Battery.BatteryLevel = battery;
+ this.botMode = deviceMode;
+ this.updateHomeKitCharacteristics();
+ } catch (e: any) {
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
+ }
+ };
+ } else {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`);
}
}
@@ -667,9 +700,8 @@ export class Bot {
await this.openAPIpushChanges();
} else {
await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`,
- );
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, pushChanges will not happen.`);
}
// Refresh the status from the API
interval(15000)
@@ -682,8 +714,9 @@ export class Bot {
async BLEpushChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`);
- if (this.On !== this.accessory.context.On || this.allowPush) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges On: ${this.On} OnCached: ${this.accessory.context.On}`);
+ const On = await this.getOn();
+ if (On !== this.accessory.context.On || this.allowPush) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges On: ${On} OnCached: ${this.accessory.context.On}`);
const switchbot = await this.platform.connectBLE();
// Convert to BLE Address
this.device.bleMac = this.device
@@ -697,27 +730,27 @@ export class Bot {
switchbot
.discover({ model: 'H', quick: true, id: this.device.bleMac })
.then(async (device_list: { press: (arg0: { id: string | undefined }) => any }[]) => {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${On}`);
return await device_list[0].press({ id: this.device.bleMac });
})
.then(() => {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
- this.accessory.context.On = this.On;
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `On: ${On} sent over BLE, sent successfully`);
+ this.accessory.context.On = On;
setTimeout(() => {
if (this.botDeviceType === 'switch') {
- this.switchService?.getCharacteristic(this.hap.Characteristic.On).updateValue(this.On);
+ this.Switch?.Service.getCharacteristic(this.hap.Characteristic.On).updateValue(On);
} else {
- this.outletService?.getCharacteristic(this.hap.Characteristic.On).updateValue(this.On);
+ this.Outlet?.Service.getCharacteristic(this.hap.Characteristic.On).updateValue(On);
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}, Switch Timeout`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${On}, Switch Timeout`);
}, 500);
})
.catch(async (e: any) => {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` +
- ` Connection & botMode: ${this.botMode}, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
+ + ` Connection & botMode: ${this.botMode}, Error Message: ${JSON.stringify(e.message)}`);
await this.BLEPushConnection();
});
} else if (this.botMode === 'switch') {
@@ -725,11 +758,11 @@ export class Bot {
switchbot
.discover({ model: 'H', quick: true, id: this.device.bleMac })
.then(async (device_list: any) => {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
- return await this.retry({
- max: this.maxRetry(),
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${On}`);
+ return await this.retryBLE({
+ max: await this.maxRetryBLE(),
fn: async () => {
- if (this.On) {
+ if (On) {
return await device_list[0].turnOn({ id: this.device.bleMac });
} else {
return await device_list[0].turnOff({ id: this.device.bleMac });
@@ -739,46 +772,43 @@ export class Bot {
})
.then(() => {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
- this.accessory.context.On = this.On;
+ this.accessory.context.On = On;
})
.catch(async (e: any) => {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` +
- ` Connection & botMode: ${this.botMode}, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
+ + ` Connection & botMode: ${this.botMode}, Error Message: ${JSON.stringify(e.message)}`);
await this.BLEPushConnection();
});
} else {
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bot Mode: ${this.botMode}`);
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + `On: ${this.On}, ` + `OnCached: ${this.accessory.context.On}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, On: ${On}, OnCached: ${this.accessory.context.On}`);
}
}
async openAPIpushChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`);
+ let On = await this.getOn();
if (this.multiPressCount > 0) {
this.debugLog(`${this.device.deviceType}: ${this.multiPressCount} request(s) queued.`);
- this.On = true;
+ On = true;
}
- if (this.On !== this.accessory.context.On || this.allowPush || this.multiPressCount > 0) {
+ if (On !== this.accessory.context.On || this.allowPush || this.multiPressCount > 0) {
let command = '';
- if (this.botMode === 'switch' && this.On) {
+ if (this.botMode === 'switch' && On) {
command = 'turnOn';
- this.On = true;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Switch Mode, Turning ${this.On}`);
- } else if (this.botMode === 'switch' && !this.On) {
+ On = true;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Switch Mode, Turning ${On}`);
+ } else if (this.botMode === 'switch' && !On) {
command = 'turnOff';
- this.On = false;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Switch Mode, Turning ${this.On}`);
+ On = false;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Switch Mode, Turning ${On}`);
} else if (this.botMode === 'press' || this.botMode === 'multipress') {
command = 'press';
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Press Mode`);
- this.On = false;
+ On = false;
} else {
throw new Error(`${this.device.deviceType}: ${this.accessory.displayName} Device Parameters not set for this Bot.`);
}
@@ -800,8 +830,10 @@ export class Bot {
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
} else {
this.statusCode(statusCode);
this.statusCode(deviceStatus.statusCode);
@@ -810,23 +842,18 @@ export class Bot {
this.multiPressCount--;
if (this.multiPressCount > 0) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Multi Press Count: ${this.multiPressCount}`);
- this.On = true;
+ On = true;
this.openAPIpushChanges();
}
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` +
- `On: ${this.On}, ` +
- `OnCached: ${this.accessory.context.On}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges,`
+ + ` On: ${On}, OnCached: ${this.accessory.context.On}`);
}
}
@@ -835,53 +862,105 @@ export class Bot {
*/
async OnSet(value: CharacteristicValue): Promise {
if (this.botDeviceType === 'garagedoor') {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetDoorState: ${value}`);
- if (value === this.hap.Characteristic.TargetDoorState.CLOSED) {
- this.On = false;
- } else {
- this.On = true;
+ if (this.GarageDoor) {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetDoorState: ${value}`);
+ if (value === this.hap.Characteristic.TargetDoorState.CLOSED) {
+ await this.setOn(false);
+ this.GarageDoor.On = false;
+ } else {
+ await this.setOn(true);
+ this.GarageDoor.On = true;
+ }
}
- } else if (
- this.botDeviceType === 'door' ||
- this.botDeviceType === 'window' ||
- this.botDeviceType === 'windowcovering'
- ) {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`);
- if (value === 0) {
- this.On = false;
- } else {
- this.On = true;
+ } else if (this.botDeviceType === 'door') {
+ if (this.Door) {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`);
+ if (value === 0) {
+ await this.setOn(false);
+ this.Door.On = false;
+ } else {
+ await this.setOn(true);
+ this.Door.On = true;
+ }
+ }
+ } else if (this.botDeviceType === 'window') {
+ if (this.Window) {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`);
+ if (value === 0) {
+ await this.setOn(false);
+ this.Window.On = false;
+ } else {
+ await this.setOn(true);
+ this.Window.On = true;
+ }
+ }
+ } else if (this.botDeviceType === 'windowcovering') {
+ if (this.WindowCovering) {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`);
+ if (value === 0) {
+ await this.setOn(false);
+ this.WindowCovering.On = false;
+ } else {
+ await this.setOn(true);
+ this.WindowCovering.On = true;
+ }
}
} else if (this.botDeviceType === 'lock') {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set LockTargetState: ${value}`);
- if (value === this.hap.Characteristic.LockTargetState.SECURED) {
- this.On = false;
- } else {
- this.On = true;
+ if (this.LockMechanism) {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set LockTargetState: ${value}`);
+ if (value === this.hap.Characteristic.LockTargetState.SECURED) {
+ await this.setOn(false);
+ this.LockMechanism.On = false;
+ } else {
+ await this.setOn(true);
+ this.LockMechanism.On = true;
+ }
}
} else if (this.botDeviceType === 'faucet') {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Active: ${value}`);
- if (value === this.hap.Characteristic.Active.INACTIVE) {
- this.On = false;
- } else {
- this.On = true;
+ if (this.Faucet) {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Active: ${value}`);
+ if (value === this.hap.Characteristic.Active.INACTIVE) {
+ await this.setOn(false);
+ this.Faucet.On = false;
+ } else {
+ await this.setOn(true);
+ this.Faucet.On = true;
+ }
}
} else if (this.botDeviceType === 'stateful') {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ProgrammableSwitchOutputState: ${value}`);
- if (value === 0) {
- this.On = false;
- } else {
- this.On = true;
+ if (this.StatefulProgrammableSwitch) {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ProgrammableSwitchOutputState: ${value}`);
+ if (value === 0) {
+ await this.setOn(false);
+ this.StatefulProgrammableSwitch.On = false;
+ } else {
+ await this.setOn(true);
+ this.StatefulProgrammableSwitch.On = true;
+ }
}
- } else {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`);
- if (this.device.bot?.mode === 'multipress') {
- if (value === true) {
- this.multiPressCount++;
- this.debugLog(`${this.device.deviceType} set to Multi-Press. Multi-Press count: ${this.multiPressCount}`);
+ } else if (this.botDeviceType === 'switch') {
+ if (this.Switch) {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ProgrammableSwitchOutputState: ${value}`);
+ if (value === 0) {
+ await this.setOn(false);
+ this.Switch.On = false;
+ } else {
+ await this.setOn(true);
+ this.Switch.On = true;
}
}
- this.On = value;
+ } else {
+ if (this.Outlet) {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`);
+ await this.setOn(Boolean(value));
+ this.Outlet.On = value;
+ }
+ }
+ if (this.device.bot?.mode === 'multipress') {
+ if (value === true) {
+ this.multiPressCount++;
+ this.debugLog(`${this.device.deviceType} set to Multi-Press. Multi-Press count: ${this.multiPressCount}`);
+ }
}
this.doBotUpdate.next();
}
@@ -892,536 +971,352 @@ export class Bot {
async updateHomeKitCharacteristics(): Promise {
// State
if (this.botDeviceType === 'garagedoor') {
- if (this.On === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ if (this.GarageDoor?.On === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.GarageDoor?.On}`);
} else {
- if (this.On) {
- this.garageDoorService?.updateCharacteristic(
- this.hap.Characteristic.TargetDoorState,
- this.hap.Characteristic.TargetDoorState.OPEN,
- );
- this.garageDoorService?.updateCharacteristic(
- this.hap.Characteristic.CurrentDoorState,
- this.hap.Characteristic.CurrentDoorState.OPEN,
- );
- this.debugLog(
- `${this.device.deviceType}: ` + `${this.accessory.displayName} updateCharacteristic TargetDoorState: Open, CurrentDoorState: Open`,
- );
+ if (this.GarageDoor.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);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` TargetDoorState: Open, CurrentDoorState: Open (${this.GarageDoor.On})`);
} else {
- this.garageDoorService?.updateCharacteristic(
- this.hap.Characteristic.TargetDoorState,
- this.hap.Characteristic.TargetDoorState.CLOSED,
- );
- this.garageDoorService?.updateCharacteristic(
- this.hap.Characteristic.CurrentDoorState,
- this.hap.Characteristic.CurrentDoorState.CLOSED,
- );
- this.debugLog(
- `${this.device.deviceType}: ` + `${this.accessory.displayName} updateCharacteristic TargetDoorState: Open, CurrentDoorState: Open`,
- );
+ 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.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc`
+ + ` TargetDoorState: Open, CurrentDoorState: Open (${this.GarageDoor.On})`);
}
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Garage Door On: ${this.On}`);
+ await this.setOn(Boolean(this.GarageDoor?.On));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Garage Door On: ${this.GarageDoor?.On}`);
} else if (this.botDeviceType === 'door') {
- if (this.On === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ if (this.Door?.On === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Door?.On}`);
} else {
- if (this.On) {
- this.doorService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100);
- this.doorService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100);
- this.doorService?.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 100, CurrentPosition: 100`);
+ if (this.Door.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);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc`
+ + ` TargetPosition: 100, CurrentPosition: 100 (${this.Door.On})`);
} else {
- this.doorService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0);
- this.doorService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0);
- this.doorService?.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 0, CurrentPosition: 0`);
+ 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);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc`
+ + ` TargetPosition: 0, CurrentPosition: 0 (${this.Door.On})`);
}
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Door On: ${this.On}`);
+ await this.setOn(Boolean(this.Door?.On));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Door On: ${this.Door?.On}`);
} else if (this.botDeviceType === 'window') {
- if (this.On === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ if (this.Window?.On === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Window?.On}`);
} else {
- if (this.On) {
- this.windowService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100);
- this.windowService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100);
- this.windowService?.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 100, CurrentPosition: 100`);
+ if (this.Window.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);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc`
+ + ` TargetPosition: 100, CurrentPosition: 100 (${this.Window.On})`);
} else {
- this.windowService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0);
- this.windowService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0);
- this.windowService?.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 0, CurrentPosition: 0`);
+ 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);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc`
+ + ` TargetPosition: 0, CurrentPosition: 0 (${this.Window.On})`);
}
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Window On: ${this.On}`);
+ await this.setOn(Boolean(this.Window?.On));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Window On: ${this.Window?.On}`);
} else if (this.botDeviceType === 'windowcovering') {
- if (this.On === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ if (this.WindowCovering?.On === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.WindowCovering?.On}`);
} else {
- if (this.On) {
- this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100);
- this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100);
- this.windowCoveringService?.updateCharacteristic(
- this.hap.Characteristic.PositionState,
- this.hap.Characteristic.PositionState.STOPPED,
- );
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 100, CurrentPosition: 100`);
+ if (this.WindowCovering.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);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc`
+ + ` TargetPosition: 100, CurrentPosition: 100 (${this.WindowCovering.On})`);
} else {
- this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, 0);
- this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 0);
- this.windowCoveringService?.updateCharacteristic(
- this.hap.Characteristic.PositionState,
- this.hap.Characteristic.PositionState.STOPPED,
- );
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: 0, CurrentPosition: 0`);
+ 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);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc`
+ + ` TargetPosition: 0, CurrentPosition: 0 (${this.WindowCovering.On})`);
}
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Window Covering On: ${this.On}`);
+ await this.setOn(Boolean(this.WindowCovering?.On));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Window Covering On: ${this.WindowCovering?.On}`);
} else if (this.botDeviceType === 'lock') {
- if (this.On === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ if (this.LockMechanism?.On === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LockMechanism?.On}`);
} else {
- if (this.On) {
- this.lockService?.updateCharacteristic(
- this.hap.Characteristic.LockTargetState,
- this.hap.Characteristic.LockTargetState.UNSECURED,
- );
- this.lockService?.updateCharacteristic(
- this.hap.Characteristic.LockCurrentState,
- this.hap.Characteristic.LockCurrentState.UNSECURED,
- );
- this.debugLog(
- `${this.device.deviceType}: ` +
- `${this.accessory.displayName} updateCharacteristic LockTargetState: UNSECURED, LockCurrentState: UNSECURED`,
- );
+ if (this.LockMechanism.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);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristicc`
+ + ` LockTargetState: UNSECURED, LockCurrentState: UNSECURED (${this.LockMechanism.On})`);
} else {
- this.lockService?.updateCharacteristic(this.hap.Characteristic.LockTargetState, this.hap.Characteristic.LockTargetState.SECURED);
- this.lockService?.updateCharacteristic(
- this.hap.Characteristic.LockCurrentState,
- this.hap.Characteristic.LockCurrentState.SECURED,
- );
- this.debugLog(
- `${this.device.deviceType}: ` + `${this.accessory.displayName} updateCharacteristic LockTargetState: SECURED, LockCurrentState: SECURED`,
- );
+ 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);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` LockTargetState: SECURED, LockCurrentState: SECURED (${this.LockMechanism.On})`);
}
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Lock On: ${this.On}`);
+ await this.setOn(Boolean(this.LockMechanism?.On));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Lock On: ${this.LockMechanism?.On}`);
} else if (this.botDeviceType === 'faucet') {
- if (this.On === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ if (this.Faucet?.On === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Faucet?.On}`);
} else {
- if (this.On) {
- this.faucetService?.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.ACTIVE);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.On}`);
+ if (this.Faucet.On) {
+ this.Faucet.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.ACTIVE);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Faucet.On}`);
} else {
- this.faucetService?.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.On}`);
+ this.Faucet.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Faucet.On}`);
}
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Faucet On: ${this.On}`);
+ await this.setOn(Boolean(this.Faucet?.On));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Faucet On: ${this.Faucet?.On}`);
} else if (this.botDeviceType === 'fan') {
- if (this.On === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ if (this.Fan?.On === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Fan?.On}`);
} else {
- if (this.On) {
- this.fanService?.updateCharacteristic(this.hap.Characteristic.On, this.On);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
+ if (this.Fan.On) {
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.On, this.Fan.On);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Fan.On}`);
} else {
- this.fanService?.updateCharacteristic(this.hap.Characteristic.On, this.On);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.On, this.Fan.On);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Fan.On}`);
}
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Fan On: ${this.On}`);
+ await this.setOn(Boolean(this.Fan?.On));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Fan On: ${this.Fan?.On}`);
} else if (this.botDeviceType === 'stateful') {
- if (this.On === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ if (this.StatefulProgrammableSwitch?.On === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.StatefulProgrammableSwitch?.On}`);
} else {
- if (this.On) {
- this.statefulProgrammableSwitchService?.updateCharacteristic(
- this.hap.Characteristic.ProgrammableSwitchEvent,
- this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS,
- );
- this.statefulProgrammableSwitchService?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 1);
- this.debugLog(
- `${this.device.deviceType}: ` +
- `${this.accessory.displayName} updateCharacteristic ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 1`,
- );
+ if (this.StatefulProgrammableSwitch.On) {
+ this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent,
+ this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS);
+ this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 1);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 1 (${this.StatefulProgrammableSwitch.On})`);
} else {
- this.statefulProgrammableSwitchService?.updateCharacteristic(
- this.hap.Characteristic.ProgrammableSwitchEvent,
- this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS,
- );
- this.statefulProgrammableSwitchService?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 0);
- this.debugLog(
- `${this.device.deviceType}: ` +
- `${this.accessory.displayName} updateCharacteristic ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 0`,
- );
+ this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent,
+ this.hap.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS);
+ this.StatefulProgrammableSwitch.Service.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, 0);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` ProgrammableSwitchEvent: SINGLE, ProgrammableSwitchOutputState: 0 (${this.StatefulProgrammableSwitch.On})`);
}
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatefulProgrammableSwitch On: ${this.On}`);
+ await this.setOn(Boolean(this.StatefulProgrammableSwitch?.On));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatefulProgrammableSwitch On: ${this.StatefulProgrammableSwitch?.On}`);
} else if (this.botDeviceType === 'switch') {
- if (this.On === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ if (this.Switch?.On === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Switch?.On}`);
} else {
- this.switchService?.updateCharacteristic(this.hap.Characteristic.On, this.On);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
+ this.Switch.Service.updateCharacteristic(this.hap.Characteristic.On, this.Switch.On);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Switch.On}`);
}
+ await this.setOn(Boolean(this.Switch?.On));
} else {
- if (this.On === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ if (this.Outlet?.On === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Outlet?.On}`);
} else {
- this.outletService?.updateCharacteristic(this.hap.Characteristic.On, this.On);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
+ this.Outlet.Service.updateCharacteristic(this.hap.Characteristic.On, this.Outlet.On);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.Outlet.On}`);
}
+ await this.setOn(Boolean(this.Outlet?.On));
}
- this.accessory.context.On = this.On;
+ this.accessory.context.On = await this.getOn();
// BatteryLevel
- if (this.BatteryLevel === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
+ if (this.Battery.BatteryLevel === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`);
} else {
- this.accessory.context.BatteryLevel = this.BatteryLevel;
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
+ this.accessory.context.BatteryLevel = this.Battery.BatteryLevel;
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`);
}
// StatusLowBattery
- if (this.StatusLowBattery === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
+ if (this.Battery.StatusLowBattery === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`);
} else {
- this.accessory.context.StatusLowBattery = this.StatusLowBattery;
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
+ this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery;
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
}
}
async removeOutletService(accessory: PlatformAccessory): Promise {
// If outletService still present, then remove first
- this.outletService = this.accessory.getService(this.hap.Service.Outlet);
- if (this.outletService) {
+ if (this.Outlet?.Service) {
+ this.Outlet.Service = this.accessory.getService(this.hap.Service.Outlet) as Service;
this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Outlet Service`);
+ accessory.removeService(this.Outlet.Service);
}
- accessory.removeService(this.outletService!);
}
async removeGarageDoorService(accessory: PlatformAccessory): Promise {
// If garageDoorService still present, then remove first
- this.garageDoorService = this.accessory.getService(this.hap.Service.GarageDoorOpener);
- if (this.garageDoorService) {
+ if (this.GarageDoor?.Service) {
+ this.GarageDoor.Service = this.accessory.getService(this.hap.Service.GarageDoorOpener) as Service;
this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Garage Door Service`);
+ accessory.removeService(this.GarageDoor.Service);
}
- accessory.removeService(this.garageDoorService!);
}
async removeDoorService(accessory: PlatformAccessory): Promise {
// If doorService still present, then remove first
- this.doorService = this.accessory.getService(this.hap.Service.Door);
- if (this.doorService) {
+ if (this.Door?.Service) {
+ this.Door.Service = this.accessory.getService(this.hap.Service.Door) as Service;
this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Door Service`);
+ accessory.removeService(this.Door.Service);
}
- accessory.removeService(this.doorService!);
}
async removeLockService(accessory: PlatformAccessory): Promise {
// If lockService still present, then remove first
- this.lockService = this.accessory.getService(this.hap.Service.LockMechanism);
- if (this.lockService) {
+ if (this.LockMechanism?.Service) {
+ this.LockMechanism.Service = this.accessory.getService(this.hap.Service.LockMechanism) as Service;
this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Lock Service`);
+ accessory.removeService(this.LockMechanism.Service);
}
- accessory.removeService(this.lockService!);
}
async removeFaucetService(accessory: PlatformAccessory): Promise {
// If faucetService still present, then remove first
- this.faucetService = this.accessory.getService(this.hap.Service.Faucet);
- if (this.faucetService) {
+ if (this.Faucet?.Service) {
+ this.Faucet.Service = this.accessory.getService(this.hap.Service.Faucet) as Service;
this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Faucet Service`);
+ accessory.removeService(this.Faucet.Service);
}
- accessory.removeService(this.faucetService!);
}
async removeFanService(accessory: PlatformAccessory): Promise {
// If fanService still present, then remove first
- this.fanService = this.accessory.getService(this.hap.Service.Fan);
- if (this.fanService) {
+ if (this.Fan?.Service) {
+ this.Fan.Service = this.accessory.getService(this.hap.Service.Fanv2) as Service;
this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Fan Service`);
+ accessory.removeService(this.Fan.Service);
}
- accessory.removeService(this.fanService!);
}
async removeWindowService(accessory: PlatformAccessory): Promise {
// If windowService still present, then remove first
- this.windowService = this.accessory.getService(this.hap.Service.Window);
- if (this.windowService) {
+ if (this.Window?.Service) {
+ this.Window.Service = this.accessory.getService(this.hap.Service.Window) as Service;
this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Window Service`);
+ accessory.removeService(this.Window.Service);
}
- accessory.removeService(this.windowService!);
}
async removeWindowCoveringService(accessory: PlatformAccessory): Promise {
// If windowCoveringService still present, then remove first
- this.windowCoveringService = this.accessory.getService(this.hap.Service.WindowCovering);
- if (this.windowCoveringService) {
+ if (this.WindowCovering?.Service) {
+ this.WindowCovering.Service = this.accessory.getService(this.hap.Service.WindowCovering) as Service;
this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Window Covering Service`);
+ accessory.removeService(this.WindowCovering.Service);
}
- accessory.removeService(this.windowCoveringService!);
}
async removeStatefulProgrammableSwitchService(accessory: PlatformAccessory): Promise {
// If statefulProgrammableSwitchService still present, then remove first
- this.statefulProgrammableSwitchService = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch);
- if (this.statefulProgrammableSwitchService) {
+ if (this.StatefulProgrammableSwitch?.Service) {
+ this.StatefulProgrammableSwitch.Service = this.accessory.getService(this.hap.Service.StatefulProgrammableSwitch) as Service;
this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Stateful Programmable Switch Service`);
+ accessory.removeService(this.StatefulProgrammableSwitch.Service);
}
- accessory.removeService(this.statefulProgrammableSwitchService!);
}
async removeSwitchService(accessory: PlatformAccessory): Promise {
// If switchService still present, then remove first
- this.switchService = this.accessory.getService(this.hap.Service.Switch);
- if (this.switchService) {
+ if (this.Switch?.Service) {
+ this.Switch.Service = this.accessory.getService(this.hap.Service.Switch) as Service;
this.warnLog(`${this.device.deviceType}: ${accessory.displayName} Removing Leftover Switch Service`);
- }
- accessory.removeService(this.switchService!);
- }
-
- private DoublePress(device: device & devicesConfig) {
- if (device.bot?.doublePress) {
- this.doublePress = device.bot?.doublePress;
- this.accessory.context.doublePress = this.doublePress;
- } else {
- this.doublePress = 1;
- }
- }
-
- async stopScanning(switchbot: any) {
- switchbot.stopScan();
- if (this.BLE_IsConnected) {
- await this.BLEparseStatus();
- await this.updateHomeKitCharacteristics();
- } else {
- await this.BLERefreshConnection(switchbot);
- }
- }
-
- async getCustomBLEAddress(switchbot: any) {
- if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
- (async () => {
- // Start to monitor advertisement packets
- await switchbot.startScan({
- model: 'H',
- });
- // Set an event handler
- switchbot.onadvertisement = (ad: any) => {
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
- };
- await sleep(10000);
- // Stop to monitor
- switchbot.stopScan();
- })();
- }
- }
-
- async BLEPushConnection() {
- if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`);
- await this.openAPIpushChanges();
- }
- }
-
- async BLERefreshConnection(switchbot: any): Promise {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:`
- + ` ${JSON.stringify(switchbot)}`);
- if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Refresh Status`);
- await this.openAPIRefreshStatus();
- }
- }
-
- async retry({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise {
- return fn().catch(async (e: any) => {
- if (max === 0) {
- throw e;
- }
- this.infoLog(e);
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`);
- await sleep(1000);
- return this.retry({ max: max - 1, fn });
- });
- }
-
- maxRetry(): number {
- if (this.device.maxRetry) {
- return this.device.maxRetry;
- } else {
- return 5;
+ accessory.removeService(this.Switch.Service);
}
}
- async PressOrSwitch(device: device & devicesConfig): Promise {
- if (!device.bot?.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,`);
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} is defaulting to "${this.botMode}" mode, you may experience issues.`);
- } else if (device.bot?.mode === 'switch') {
- this.botMode = 'switch';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Bot Mode: ${this.botMode}`);
- } else if (device.bot?.mode === 'press') {
- this.botMode = 'press';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Bot Mode: ${this.botMode}`);
- } else if (device.bot?.mode === 'multipress') {
- this.botMode = 'multipress';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Bot Mode: ${this.botMode}`);
- } else {
- throw new Error(`${this.device.deviceType}: ${this.accessory.displayName} Bot Mode: ${this.botMode}`);
- }
- }
-
- async allowPushChanges(device: device & devicesConfig): Promise {
- if (device.bot?.allowPush) {
- this.allowPush = true;
+ async getOn(): Promise {
+ let On: boolean;
+ if (this.botDeviceType === 'garagedoor') {
+ On = this.GarageDoor?.On ? true : false;
+ } else if (this.botDeviceType === 'door') {
+ On = this.Door?.On ? true : false;
+ } else if (this.botDeviceType === 'window') {
+ On = this.Window?.On ? true : false;
+ } else if (this.botDeviceType === 'windowcovering') {
+ On = this.WindowCovering?.On ? true : false;
+ } else if (this.botDeviceType === 'lock') {
+ On = this.LockMechanism?.On ? true : false;
+ } else if (this.botDeviceType === 'faucet') {
+ On = this.Faucet?.On ? true : false;
+ } else if (this.botDeviceType === 'fan') {
+ On = this.Fan?.On ? true : false;
+ } else if (this.botDeviceType === 'stateful') {
+ On = this.StatefulProgrammableSwitch?.On ? true : false;
+ } else if (this.botDeviceType === 'switch') {
+ On = this.Switch?.On ? true : false;
} else {
- this.allowPush = false;
+ On = this.Outlet?.On ? true : false;
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Allowing Push Changes: ${this.allowPush}`);
+ return On;
}
- async scan(device: device & devicesConfig): Promise {
- if (device.scanDuration) {
- this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
- }
- } else {
- this.scanDuration = this.accessory.context.scanDuration = 1;
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
- }
- }
- }
-
- async statusCode(statusCode: number): Promise {
- switch (statusCode) {
- case 151:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
- break;
- case 152:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
- break;
- case 160:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
- break;
- case 161:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
- this.offlineOff();
- break;
- case 171:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` +
- `Hub: ${this.device.hubDeviceId}`,
- );
- this.offlineOff();
- break;
- case 190:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
- ` Or command format is invalid, statusCode: ${statusCode}`,
- );
- break;
- case 100:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
- break;
- case 200:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
- break;
- case 400:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. `
- + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`);
- break;
- case 401:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, `
- + `but the request has not been authenticated, statusCode: ${statusCode}`);
- break;
- case 403:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not `
- + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`);
- break;
- case 404:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, `
- + `statusCode: ${statusCode}`);
- break;
- case 406:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via `
- + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`);
- break;
- case 415:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType `
- + `header that is not supported by the server, statusCode: ${statusCode}`);
- break;
- case 422:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, The client has made a valid request, `
- + `but the server cannot process it. This is often used for APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
- break;
- case 429:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of `
- + `requests allowed for a given time window, statusCode: ${statusCode}`);
- break;
- case 500:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings `
- + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`);
- break;
- default:
- this.infoLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` +
- `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`,
- );
- }
- }
-
- async offlineOff(): Promise {
- if (this.device.offline) {
- await this.deviceContext();
- await this.updateHomeKitCharacteristics();
- }
- }
-
- async apiError(e: any): Promise {
+ async setOn(On: boolean): Promise {
if (this.botDeviceType === 'garagedoor') {
- this.garageDoorService?.updateCharacteristic(this.hap.Characteristic.TargetDoorState, e);
- this.garageDoorService?.updateCharacteristic(this.hap.Characteristic.CurrentDoorState, e);
- this.garageDoorService?.updateCharacteristic(this.hap.Characteristic.ObstructionDetected, e);
+ if (this.GarageDoor) {
+ this.GarageDoor.On = On;
+ }
} else if (this.botDeviceType === 'door') {
- this.doorService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, e);
- this.doorService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e);
- this.doorService?.updateCharacteristic(this.hap.Characteristic.PositionState, e);
+ if (this.Door) {
+ this.Door.On = On;
+ }
} else if (this.botDeviceType === 'window') {
- this.windowService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, e);
- this.windowService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e);
- this.windowService?.updateCharacteristic(this.hap.Characteristic.PositionState, e);
+ if (this.Window) {
+ this.Window.On = On;
+ }
} else if (this.botDeviceType === 'windowcovering') {
- this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.TargetPosition, e);
- this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e);
- this.windowCoveringService?.updateCharacteristic(this.hap.Characteristic.PositionState, e);
+ if (this.WindowCovering) {
+ this.WindowCovering.On = On;
+ }
} else if (this.botDeviceType === 'lock') {
- this.doorService?.updateCharacteristic(this.hap.Characteristic.LockTargetState, e);
- this.doorService?.updateCharacteristic(this.hap.Characteristic.LockCurrentState, e);
+ if (this.LockMechanism) {
+ this.LockMechanism.On = On;
+ }
} else if (this.botDeviceType === 'faucet') {
- this.faucetService?.updateCharacteristic(this.hap.Characteristic.Active, e);
+ if (this.Faucet) {
+ this.Faucet.On = On;
+ }
} else if (this.botDeviceType === 'fan') {
- this.fanService?.updateCharacteristic(this.hap.Characteristic.On, e);
+ if (this.Fan) {
+ this.Fan.On = On;
+ }
} else if (this.botDeviceType === 'stateful') {
- this.statefulProgrammableSwitchService?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchEvent, e);
- this.statefulProgrammableSwitchService?.updateCharacteristic(this.hap.Characteristic.ProgrammableSwitchOutputState, e);
+ if (this.StatefulProgrammableSwitch) {
+ this.StatefulProgrammableSwitch.On = On;
+ }
} else if (this.botDeviceType === 'switch') {
- this.switchService?.updateCharacteristic(this.hap.Characteristic.On, e);
+ if (this.Switch) {
+ this.Switch.On = On;
+ }
} else {
- this.outletService?.updateCharacteristic(this.hap.Characteristic.On, e);
+ if (this.Outlet) {
+ this.Outlet.On = On;
+ }
}
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e);
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e);
}
- async deviceType(device: device & devicesConfig): Promise {
+ async getBotConfigSettings(device: device & devicesConfig) {
+ //Bot Device Type
if (!device.bot?.deviceType && this.accessory.context.deviceType) {
this.botDeviceType = this.accessory.context.deviceType;
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Type: ${this.botDeviceType}, from Accessory Cache.`);
@@ -1435,41 +1330,33 @@ export class Bot {
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} No Device Type Set, deviceType: ${this.device.bot?.deviceType}`);
this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using default deviceType: ${this.botDeviceType}`);
}
- }
-
- async deviceContext() {
- if (this.On === undefined) {
- this.On = false;
- this.accessory.context.On = this.On;
- } else {
- this.On = this.accessory.context.On;
- }
- if (this.BatteryLevel === undefined) {
- this.BatteryLevel = 100;
+ // Bot Mode
+ if (!device.bot?.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,`);
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} is defaulting to "${this.botMode}" mode, you may experience issues.`);
+ } else if (device.bot?.mode === 'switch') {
+ this.botMode = 'switch';
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Bot Mode: ${this.botMode}`);
+ } else if (device.bot?.mode === 'press') {
+ this.botMode = 'press';
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Bot Mode: ${this.botMode}`);
+ } else if (device.bot?.mode === 'multipress') {
+ this.botMode = 'multipress';
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Bot Mode: ${this.botMode}`);
} else {
- this.BatteryLevel = this.accessory.context.BatteryLevel;
+ throw new Error(`${this.device.deviceType}: ${this.accessory.displayName} Bot Mode: ${this.botMode}`);
}
- if (this.StatusLowBattery === undefined) {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
- this.accessory.context.StatusLowBattery = this.StatusLowBattery;
+
+ // Bot Double Press
+ if (device.bot?.doublePress) {
+ this.doublePress = device.bot?.doublePress;
+ this.accessory.context.doublePress = this.doublePress;
} else {
- this.StatusLowBattery = this.accessory.context.StatusLowBattery;
- }
- if (this.FirmwareRevision === undefined) {
- this.FirmwareRevision = this.platform.version;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
+ this.doublePress = 1;
}
- }
- async refreshRate(device: device & devicesConfig): Promise {
- if (device.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
- } else if (this.platform.config.options!.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
- }
- // pushRatePress
+ // Bot Press PushRate
if (device?.bot?.pushRatePress) {
this.pushRatePress = device?.bot?.pushRatePress;
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Bot pushRatePress: ${this.pushRatePress}`);
@@ -1477,106 +1364,144 @@ export class Bot {
this.pushRatePress = 15;
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default Bot pushRatePress: ${this.pushRatePress}`);
}
- }
-
- async deviceConfig(device: device & devicesConfig): Promise {
- let config = {};
- if (device.bot) {
- config = device.bot;
- }
- if (device.connectionType !== undefined) {
- config['connectionType'] = device.connectionType;
- }
- if (device.external !== undefined) {
- config['external'] = device.external;
- }
- if (device.logging !== undefined) {
- config['logging'] = device.logging;
- }
- if (device.refreshRate !== undefined) {
- config['refreshRate'] = device.refreshRate;
- }
- if (device.scanDuration !== undefined) {
- config['scanDuration'] = device.scanDuration;
- }
- if (device.offline !== undefined) {
- config['offline'] = device.offline;
- }
- if (device.maxRetry !== undefined) {
- config['maxRetry'] = device.maxRetry;
- }
- if (device.webhook !== undefined) {
- config['webhook'] = device.webhook;
- }
- if (Object.entries(config).length !== 0) {
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
- }
- }
- async deviceLogs(device: device & devicesConfig): Promise {
- if (this.platform.debugMode) {
- this.deviceLogging = this.accessory.context.logging = 'debugMode';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
- } else if (device.logging) {
- this.deviceLogging = this.accessory.context.logging = device.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
- } else if (this.platform.config.options?.logging) {
- this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
+ // Bot Allow Push
+ if (device.bot?.allowPush) {
+ this.allowPush = true;
} else {
- this.deviceLogging = this.accessory.context.logging = 'standard';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
+ this.allowPush = false;
}
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Allowing Push Changes: ${this.allowPush}`);
+ // Bot Multi Press Count
+ this.multiPressCount = 0;
}
- /**
- * Logging for Device
- */
- infoLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.info(String(...log));
+ async BLEPushConnection() {
+ if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`);
+ await this.openAPIpushChanges();
}
}
- warnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.warn(String(...log));
+ async BLERefreshConnection(switchbot: any): Promise {
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:`
+ + ` ${JSON.stringify(switchbot)}`);
+ if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Refresh Status`);
+ await this.openAPIRefreshStatus();
}
}
- debugWarnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.warn('[DEBUG]', String(...log));
+ 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);
+ }
}
}
}
- errorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.error(String(...log));
- }
- }
-
- debugErrorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.error('[DEBUG]', String(...log));
+ async apiError(e: any): Promise {
+ 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);
}
- }
- }
-
- debugLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging === 'debug') {
- this.platform.log.info('[DEBUG]', String(...log));
- } else {
- this.platform.log.debug(String(...log));
+ } 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);
}
}
- }
-
- enablingDeviceLogging(): boolean {
- return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard';
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e);
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e);
}
}
diff --git a/src/device/ceilinglight.ts b/src/device/ceilinglight.ts
index a720d1d3..d73e7348 100644
--- a/src/device/ceilinglight.ts
+++ b/src/device/ceilinglight.ts
@@ -1,143 +1,112 @@
+/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+ *
+ * ceilinglight.ts: @switchbot/homebridge-switchbot.
+ */
import { request } from 'undici';
-import { sleep } from '../utils.js';
+import { deviceBase } from './device.js';
import { interval, Subject } from 'rxjs';
-import { SwitchBotPlatform } from '../platform.js';
+import { Devices } from '../settings.js';
+import { hs2rgb, rgb2hs, m2hs } from '../utils.js';
import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
-import { device, devicesConfig, deviceStatus, hs2rgb, rgb2hs, m2hs, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js';
-import {
- Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap, API, Logging, HAP,
-} from 'homebridge';
+
+import type { SwitchBotPlatform } from '../platform.js';
+import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js';
+import type { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge';
/**
* Platform Accessory
* An instance of this class is created for each accessory your platform registers
* Each accessory may expose multiple services of different service types.
*/
-export class CeilingLight {
- public readonly api: API;
- public readonly log: Logging;
- public readonly config!: SwitchBotPlatformConfig;
- protected readonly hap: HAP;
+export class CeilingLight extends deviceBase {
// Services
- lightBulbService!: Service;
-
- // Characteristic Values
- On!: CharacteristicValue;
- Hue!: CharacteristicValue;
- Saturation!: CharacteristicValue;
- Brightness!: CharacteristicValue;
- ColorTemperature?: CharacteristicValue;
- FirmwareRevision!: CharacteristicValue;
-
- // OpenAPI Status
- OpenAPI_On: deviceStatus['power'];
- OpenAPI_RGB: deviceStatus['color'];
- OpenAPI_Brightness: deviceStatus['brightness'];
- OpenAPI_FirmwareRevision: deviceStatus['version'];
- OpenAPI_ColorTemperature?: deviceStatus['colorTemperature'];
-
- // BLE Status
- BLE_Delay: serviceData['delay'];
- BLE_On: serviceData['state'];
- BLE_WifiRssi: serviceData['wifiRssi'];
-
- // BLE Others
- BLE_IsConnected?: boolean;
-
- // Config
- set_minStep?: number;
- scanDuration!: number;
- deviceLogging!: string;
- deviceRefreshRate!: number;
- adaptiveLightingShift?: number;
+ private LightBulb: {
+ Name: CharacteristicValue;
+ Service: Service;
+ On: CharacteristicValue;
+ Hue: CharacteristicValue;
+ Saturation: CharacteristicValue;
+ Brightness: CharacteristicValue;
+ ColorTemperature?: CharacteristicValue;
+ };
// Adaptive Lighting
AdaptiveLightingController?: ControllerConstructor | Controller;
- minKelvin!: number;
- maxKelvin!: number;
-
- // Others
- cacheKelvin!: number;
+ adaptiveLightingShift?: number;
// Updates
ceilingLightUpdateInProgress!: boolean;
doCeilingLightUpdate!: Subject;
- // Connection
- private readonly OpenAPI: boolean;
- private readonly BLE: boolean;
-
constructor(
- private readonly platform: SwitchBotPlatform,
- private accessory: PlatformAccessory,
- public device: device & devicesConfig,
+ readonly platform: SwitchBotPlatform,
+ accessory: PlatformAccessory,
+ device: device & devicesConfig,
) {
- this.api = this.platform.api;
- this.log = this.platform.log;
- this.config = this.platform.config;
- this.hap = this.api.hap;
- // Connection
- this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI';
- this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI';
+ super(platform, accessory, device);
// default placeholders
- this.deviceLogs(device);
- this.scan(device);
- this.refreshRate(device);
this.adaptiveLighting(device);
- this.deviceContext();
- this.deviceConfig(device);
// this is subject we use to track when we need to POST changes to the SwitchBot API
this.doCeilingLightUpdate = new Subject();
this.ceilingLightUpdateInProgress = false;
- // Retrieve initial values and updateHomekit
- this.refreshStatus();
-
- // set accessory information
- accessory
- .getService(this.hap.Service.AccessoryInformation)!
- .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
- .setCharacteristic(this.hap.Characteristic.Model, this.model(device))
- .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId)
- .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
-
- // get the Lightbulb service if it exists, otherwise create a new Lightbulb service
- // you can create multiple services for each accessory
- const lightBulbService = `${accessory.displayName} ${device.deviceType}`;
- (this.lightBulbService = accessory.getService(this.hap.Service.Lightbulb)
- || accessory.addService(this.hap.Service.Lightbulb)), lightBulbService;
-
- if (this.adaptiveLightingShift === -1 && this.accessory.context.adaptiveLighting) {
- this.accessory.removeService(this.lightBulbService);
- this.lightBulbService = this.accessory.addService(this.hap.Service.Lightbulb);
- this.accessory.context.adaptiveLighting = false;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting}`);
+ // Initialize LightBulb Service
+ accessory.context.LightBulb = accessory.context.LightBulb ?? {};
+ this.LightBulb = {
+ Name: accessory.context.LightBul.bName ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service,
+ On: accessory.context.On ?? false,
+ Hue: accessory.context.Hue ?? 0,
+ Saturation: accessory.context.Saturation ?? 0,
+ Brightness: accessory.context.Brightness ?? 0,
+ ColorTemperature: accessory.context.ColorTemperature ?? 140,
+ };
+ accessory.context.LightBulb = this.LightBulb as object;
+
+ // Adaptive Lighting
+ if (this.adaptiveLightingShift === -1 && accessory.context.adaptiveLighting) {
+ accessory.removeService(this.LightBulb.Service);
+ this.LightBulb.Service = accessory.addService(this.hap.Service.Lightbulb);
+ accessory.context.adaptiveLighting = false;
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} adaptiveLighting: ${accessory.context.adaptiveLighting}`);
}
-
- this.lightBulbService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.lightBulbService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.lightBulbService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
+ if (this.adaptiveLightingShift !== -1) {
+ this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.LightBulb.Service, {
+ customTemperatureAdjustment: this.adaptiveLightingShift,
+ });
+ this.accessory.configureController(this.AdaptiveLightingController);
+ this.accessory.context.adaptiveLighting = true;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting},`
+ + ` adaptiveLightingShift: ${this.adaptiveLightingShift}`);
}
- // handle on / off events using the On characteristic
- this.lightBulbService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`);
- // handle Brightness events using the Brightness characteristic
- this.lightBulbService
+ // Initialize LightBulb Characteristics
+ this.LightBulb.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.LightBulb.Name)
+ .getCharacteristic(this.hap.Characteristic.On)
+ .onGet(() => {
+ return this.LightBulb.On;
+ })
+ .onSet(this.OnSet.bind(this));
+
+ // Initialize LightBulb Brightness
+ this.LightBulb.Service
.getCharacteristic(this.hap.Characteristic.Brightness)
.setProps({
- minStep: this.minStep(device),
+ minStep: device.ceilinglight?.set_minStep ?? 1,
minValue: 0,
maxValue: 100,
validValueRanges: [0, 100],
})
.onGet(() => {
- return this.Brightness;
+ return this.LightBulb.Brightness;
})
.onSet(this.BrightnessSet.bind(this));
- // handle ColorTemperature events using the ColorTemperature characteristic
- this.lightBulbService
+ // Initialize LightBulb ColorTemperature
+ this.LightBulb.Service
.getCharacteristic(this.hap.Characteristic.ColorTemperature)
.setProps({
minValue: 140,
@@ -145,12 +114,12 @@ export class CeilingLight {
validValueRanges: [140, 500],
})
.onGet(() => {
- return this.ColorTemperature!;
+ return this.LightBulb.ColorTemperature!;
})
.onSet(this.ColorTemperatureSet.bind(this));
- // handle Hue events using the Hue characteristic
- this.lightBulbService
+ // Initialize LightBulb Hue
+ this.LightBulb.Service
.getCharacteristic(this.hap.Characteristic.Hue)
.setProps({
minValue: 0,
@@ -158,12 +127,12 @@ export class CeilingLight {
validValueRanges: [0, 360],
})
.onGet(() => {
- return this.Hue;
+ return this.LightBulb.Hue;
})
.onSet(this.HueSet.bind(this));
- // handle Hue events using the Hue characteristic
- this.lightBulbService
+ // Initialize LightBulb Saturation
+ this.LightBulb.Service
.getCharacteristic(this.hap.Characteristic.Saturation)
.setProps({
minValue: 0,
@@ -171,22 +140,12 @@ export class CeilingLight {
validValueRanges: [0, 100],
})
.onGet(() => {
- return this.Saturation;
+ return this.LightBulb.Saturation;
})
.onSet(this.SaturationSet.bind(this));
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`);
- if (this.adaptiveLightingShift !== -1) {
- this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.lightBulbService, {
- customTemperatureAdjustment: this.adaptiveLightingShift,
- });
- this.accessory.configureController(this.AdaptiveLightingController);
- this.accessory.context.adaptiveLighting = true;
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting},` +
- ` adaptiveLightingShift: ${this.adaptiveLightingShift}`,
- );
- }
+ // Retrieve initial values and updateHomekit
+ this.refreshStatus();
// Update Homekit
this.updateHomeKitCharacteristics();
@@ -199,27 +158,7 @@ export class CeilingLight {
});
//regisiter webhook event handler
- if (this.device.webhook) {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`);
- this.platform.webhookEventHandler[this.device.deviceId] = async (context) => {
- try {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
- const { powerState, brightness, colorTemperature } = context;
- const { On, Brightness, ColorTemperature } = this;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` +
- '(powerState, brightness, colorTemperature) = ' +
- `Webhook:(${powerState}, ${brightness}, ${colorTemperature}), ` +
- `current:(${On}, ${Brightness}, ${ColorTemperature})`);
- this.On = powerState === 'ON' ? true : false;
- this.Brightness = brightness;
- this.ColorTemperature = colorTemperature;
- this.updateHomeKitCharacteristics();
- } catch (e: any) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
- + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
- }
- };
- }
+ this.registerWebhook(accessory, device);
// Watch for Bulb change events
// We put in a debounce of 100ms so we don't make duplicate calls
@@ -228,117 +167,100 @@ export class CeilingLight {
tap(() => {
this.ceilingLightUpdateInProgress = true;
}),
- debounceTime(this.platform.config.options!.pushRate! * 1000),
+ debounceTime(this.devicePushRate * 1000),
)
.subscribe(async () => {
try {
await this.pushChanges();
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` +
- ` Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,`
+ + ` Error Message: ${JSON.stringify(e.message)}`);
}
this.ceilingLightUpdateInProgress = false;
});
}
- private model(device): CharacteristicValue {
- let model: string;
- if (device.deviceType === 'Ceiling Light') {
- model = 'W2612230' || 'W2612240';
- } else if (device.deviceType === 'Ceiling Light Pro') {
- model = 'W2612210' || 'W2612220';
- } else {
- model = 'unknown';
- }
- return model;
- }
-
/**
- * Parse the device status from the SwitchBot api
+ * Parse the device status from the SwitchBotBLE API
*/
- async parseStatus(): Promise {
- if (!this.device.enableCloudService && this.OpenAPI) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`);
- }
- /*if (this.BLE) {
- await this.BLEparseStatus();
- } else*/ if (this.OpenAPI && this.platform.config.credentials?.token) {
- await this.openAPIparseStatus();
- } else {
- await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`,
- );
- }
- }
-
- async BLEparseStatus(): Promise {
+ async BLEparseStatus(serviceData: serviceData): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
// State
- switch (this.BLE_On) {
+ switch (serviceData.state) {
case 'on':
- this.On = true;
+ this.LightBulb.On = true;
break;
default:
- this.On = false;
+ this.LightBulb.On = false;
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`);
}
- async openAPIparseStatus() {
+ /**
+ * Parse the device status from the SwitchBot OpenAPI
+ */
+ async openAPIparseStatus(deviceStatus: deviceStatus) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
- switch (this.OpenAPI_On) {
+ switch (deviceStatus.body.power) {
case 'on':
- this.On = true;
+ this.LightBulb.On = true;
break;
default:
- this.On = false;
+ this.LightBulb.On = false;
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`);
// Brightness
- this.Brightness = Number(this.OpenAPI_Brightness);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.Brightness}`);
+ this.LightBulb.Brightness = Number(deviceStatus.body.brightness);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`);
// Color, Hue & Brightness
- if (this.OpenAPI_RGB) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(this.OpenAPI_RGB)}`);
- const [red, green, blue] = this.OpenAPI_RGB!.split(':');
+ if (deviceStatus.body.color) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(deviceStatus.body.color)}`);
+ const [red, green, blue] = deviceStatus.body.color!.split(':');
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} red: ${JSON.stringify(red)}`);
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} green: ${JSON.stringify(green)}`);
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${JSON.stringify(blue)}`);
const [hue, saturation] = rgb2hs(Number(red), Number(green), Number(blue));
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`);
// Hue
- this.Hue = hue;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`);
+ this.LightBulb.Hue = hue;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`);
// Saturation
- this.Saturation = saturation;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`);
+ this.LightBulb.Saturation = saturation;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`);
}
// ColorTemperature
- if (!Number.isNaN(this.OpenAPI_ColorTemperature)) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} OpenAPI ColorTemperature: ${this.OpenAPI_ColorTemperature}`);
- const mired = Math.round(1000000 / this.OpenAPI_ColorTemperature!);
+ if (!Number.isNaN(deviceStatus.body.colorTemperature)) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} OpenAPI ColorTemperature: ${deviceStatus.body.colorTemperature}`);
+ const mired = Math.round(1000000 / deviceStatus.body.colorTemperature!);
- this.ColorTemperature = Number(mired);
+ this.LightBulb.ColorTemperature = Number(mired);
- this.ColorTemperature = Math.max(Math.min(this.ColorTemperature, 500), 140);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`);
+ this.LightBulb.ColorTemperature = Math.max(Math.min(this.LightBulb.ColorTemperature, 500), 140);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`);
}
- // FirmwareRevision
- this.FirmwareRevision = this.OpenAPI_FirmwareRevision!;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
+ // Firmware Version
+ const version = deviceStatus.body.version?.toString();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
+ if (deviceStatus.body.version) {
+ const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
+ this.accessory
+ .getService(this.hap.Service.AccessoryInformation)!
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
+ .updateValue(deviceVersion);
+ this.accessory.context.deviceVersion = deviceVersion;
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
+ }
}
/**
@@ -353,10 +275,8 @@ export class CeilingLight {
await this.openAPIRefreshStatus();
} else {
await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` +
- ` ${this.device.connectionType}, refreshStatus will not happen.`,
- );
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
}
}
@@ -373,110 +293,43 @@ export class CeilingLight {
// Start to monitor advertisement packets
(async () => {
// Start to monitor advertisement packets
- await switchbot.startScan({
- id: this.device.bleMac,
- });
+ await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac });
// Set an event handler
switchbot.onadvertisement = (ad: any) => {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
- if (this.device.bleMac === ad.address && ad.model === 's') {
+ if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.BLE_On = ad.serviceData.state;
- //this.delay = ad.serviceData.delay;
- //this.timer = ad.serviceData.timer;
- //this.syncUtcTime = ad.serviceData.syncUtcTime;
- //this.wifiRssi = ad.serviceData.wifiRssi;
- //this.overload = ad.serviceData.overload;
- //this.currentPower = ad.serviceData.currentPower;
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
}
};
- // Wait 1 seconds
+ // Wait 10 seconds
await switchbot.wait(this.scanDuration * 1000);
// Stop to monitor
await switchbot.stopScan();
// Update HomeKit
- await this.BLEparseStatus();
+ await this.BLEparseStatus(switchbot.onadvertisement.serviceData);
await this.updateHomeKitCharacteristics();
})();
- /*if (switchbot !== false) {
- switchbot
- .startScan({
- model: '',
- id: this.device.bleMac,
- })
- .then(async () => {
- // Set an event handler
- switchbot.onadvertisement = async (ad: ad) => {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` +
- ` BLE Address Found: ${ad.address}`,
- );
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.BLE_On = ad.serviceData.state;
- //this.delay = ad.serviceData.delay;
- //this.timer = ad.serviceData.timer;
- //this.syncUtcTime = ad.serviceData.syncUtcTime;
- //this.wifiRssi = ad.serviceData.wifiRssi;
- //this.overload = ad.serviceData.overload;
- //this.currentPower = ad.serviceData.currentPower;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} state: ${ad.serviceData.state}, ` +
- `delay: ${ad.serviceData.delay}, timer: ${ad.serviceData.timer}, syncUtcTime: ${ad.serviceData.syncUtcTime} ` +
- `wifiRssi: ${ad.serviceData.wifiRssi}, overload: ${ad.serviceData.overload}, currentPower: ${ad.serviceData.currentPower}`,
- );
-
- if (ad.serviceData) {
- this.BLE_IsConnected = true;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
- await this.stopScanning(switchbot);
- } else {
- this.BLE_IsConnected = false;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
- }
- };
- // Wait
- return await sleep(this.scanDuration * 1000);
- })
- .then(async () => {
- // Stop to monitor
- await this.stopScanning(switchbot);
- })
- .catch(async (e: any) => {
- this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
- await this.BLERefreshConnection(switchbot);
- });
- } else {
+ if (switchbot === undefined) {
await this.BLERefreshConnection(switchbot);
- }*/
+ }
}
- async openAPIRefreshStatus() {
+ async openAPIRefreshStatus(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
try {
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, {
- headers: this.platform.generateHeaders(),
- });
+ const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries,
+ `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() });
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
const deviceStatus: any = await body.json();
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
- this.OpenAPI_On = deviceStatus.body.power;
- this.OpenAPI_RGB = deviceStatus.body.color;
- this.OpenAPI_Brightness = deviceStatus.body.brightness;
- this.OpenAPI_ColorTemperature = deviceStatus.body.colorTemperature;
- this.OpenAPI_FirmwareRevision = deviceStatus.body.version;
- this.openAPIparseStatus();
+ this.openAPIparseStatus(deviceStatus);
this.updateHomeKitCharacteristics();
} else {
this.statusCode(statusCode);
@@ -484,10 +337,55 @@ export class CeilingLight {
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
+ }
+ }
+
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
+ if (device.webhook) {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`);
+ this.platform.webhookEventHandler[device.deviceId] = async (context) => {
+ try {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
+ const { powerState, brightness, colorTemperature } = context;
+ const { On, Brightness, ColorTemperature } = this.LightBulb;
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} ` +
+ '(powerState, brightness, colorTemperature) = ' +
+ `Webhook:(${powerState}, ${brightness}, ${colorTemperature}), `
+ + `current:(${On}, ${Brightness}, ${ColorTemperature})`);
+
+ // On
+ this.LightBulb.On = powerState === 'ON' ? true : false;
+ if (accessory.context.Brightness !== this.LightBulb.On) {
+ this.infoLog(`${device.deviceType}: ${accessory.displayName} On: ${this.LightBulb.On}`);
+ } else {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} On: ${this.LightBulb.On}`);
+ }
+
+ // Brightness
+ this.LightBulb.Brightness = brightness;
+ if (accessory.context.Brightness !== this.LightBulb.Brightness) {
+ this.infoLog(`${device.deviceType}: ${accessory.displayName} Brightness: ${this.LightBulb.Brightness}`);
+ } else {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} Brightness: ${this.LightBulb.Brightness}`);
+ }
+
+ // ColorTemperature
+ this.LightBulb.ColorTemperature = colorTemperature;
+ if (accessory.context.ColorTemperature !== this.LightBulb.ColorTemperature) {
+ this.infoLog(`${device.deviceType}: ${accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`);
+ } else {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`);
+ }
+ this.updateHomeKitCharacteristics();
+ } catch (e: any) {
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} `
+ + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
+ }
+ };
+ } else {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`);
}
}
@@ -511,9 +409,8 @@ export class CeilingLight {
await this.openAPIpushChanges();
} else {
await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`,
- );
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, pushChanges will not happen.`);
}
// Refresh the status from the API
interval(15000)
@@ -526,8 +423,9 @@ export class CeilingLight {
async BLEpushChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`);
- if (this.On !== this.accessory.context.On) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges On: ${this.On} OnCached: ${this.accessory.context.On}`);
+ if (this.LightBulb.On !== this.accessory.context.On) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` BLEpushChanges On: ${this.LightBulb.On} OnCached: ${this.accessory.context.On}`);
const switchbot = await this.platform.connectBLE();
// Convert to BLE Address
this.device.bleMac = this.device
@@ -541,11 +439,11 @@ export class CeilingLight {
id: this.device.bleMac,
})
.then(async (device_list: any) => {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
- return await this.retry({
- max: this.maxRetry(),
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`);
+ return await this.retryBLE({
+ max: await this.maxRetryBLE(),
fn: async () => {
- if (this.On) {
+ if (this.LightBulb.On) {
return await device_list[0].turnOn({ id: this.device.bleMac });
} else {
return await device_list[0].turnOff({ id: this.device.bleMac });
@@ -555,28 +453,28 @@ export class CeilingLight {
})
.then(() => {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
- this.On = false;
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `On: ${this.LightBulb.On} sent over BLE, sent successfully`);
+ this.LightBulb.On = false;
})
.catch(async (e: any) => {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
await this.BLEPushConnection();
});
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + `On: ${this.On}, ` + `OnCached: ${this.accessory.context.On}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` No BLEpushChanges: On: ${this.LightBulb.On}, `
+ + `OnCached: ${this.accessory.context.On}`);
}
}
async openAPIpushChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`);
- if (this.On !== this.accessory.context.On) {
+ if (this.LightBulb.On !== this.accessory.context.On) {
let command = '';
- if (this.On) {
+ if (this.LightBulb.On) {
command = 'turnOn';
} else {
command = 'turnOff';
@@ -599,46 +497,44 @@ export class CeilingLight {
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
} else {
this.statusCode(statusCode);
this.statusCode(deviceStatus.statusCode);
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` +
- `On: ${this.On}, ` +
- `OnCached: ${this.accessory.context.On}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.`
+ + `On: ${this.LightBulb.On}, `
+ + `OnCached: ${this.accessory.context.On}`);
}
// Push Hue & Saturation Update
- if (this.On) {
+ if (this.LightBulb.On) {
await this.pushHueSaturationChanges();
}
// Push ColorTemperature Update
- if (this.On) {
+ if (this.LightBulb.On) {
await this.pushColorTemperatureChanges();
}
// Push Brightness Update
- if (this.On) {
+ if (this.LightBulb.On) {
await this.pushBrightnessChanges();
}
}
async pushHueSaturationChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushHueSaturationChanges`);
- if (this.Hue !== this.accessory.context.Hue || this.Saturation !== this.accessory.context.Saturation) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.Hue)}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.Saturation)}`);
- const [red, green, blue] = hs2rgb(Number(this.Hue), Number(this.Saturation));
+ if (this.LightBulb.Hue !== this.accessory.context.Hue || this.LightBulb.Saturation !== this.accessory.context.Saturation) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.LightBulb.Hue)}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.LightBulb.Saturation)}`);
+ const [red, green, blue] = hs2rgb(Number(this.LightBulb.Hue), Number(this.LightBulb.Saturation));
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} rgb: ${JSON.stringify([red, green, blue])}`);
const bodyChange = JSON.stringify({
command: 'setColor',
@@ -658,32 +554,30 @@ export class CeilingLight {
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
} else {
this.statusCode(statusCode);
this.statusCode(deviceStatus.statusCode);
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushHueSaturationChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushHueSaturationChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.Hue}, ` +
- `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.LightBulb.Hue}, HueCached: `
+ + `${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`);
}
}
async pushColorTemperatureChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushColorTemperatureChanges`);
- if (this.ColorTemperature !== this.accessory.context.ColorTemperature) {
- const kelvin = Math.round(1000000 / Number(this.ColorTemperature));
- this.cacheKelvin = kelvin;
+ 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({
command: 'setColorTemperature',
parameter: `${kelvin}`,
@@ -702,7 +596,7 @@ export class CeilingLight {
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
} else {
this.statusCode(statusCode);
@@ -710,25 +604,21 @@ export class CeilingLight {
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushColorTemperatureChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushColorTemperatureChanges with`
+ + ` ${this.device.connectionType} Connection, Error Message: ${JSON.stringify(e.message)}`);
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No pushColorTemperatureChanges.` +
- `ColorTemperature: ${this.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushColorTemperatureChanges.`
+ + `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`);
}
}
async pushBrightnessChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushBrightnessChanges`);
- if (this.Brightness !== this.accessory.context.Brightness) {
+ if (this.LightBulb.Brightness !== this.accessory.context.Brightness) {
const bodyChange = JSON.stringify({
command: 'setBrightness',
- parameter: `${this.Brightness}`,
+ parameter: `${this.LightBulb.Brightness}`,
commandType: 'command',
});
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`);
@@ -744,25 +634,23 @@ export class CeilingLight {
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
} else {
this.statusCode(statusCode);
this.statusCode(deviceStatus.statusCode);
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushBrightnessChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushBrightnessChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No pushBrightnessChanges.` +
- `Brightness: ${this.Brightness}, ` +
- `BrightnessCached: ${this.accessory.context.Brightness}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushBrightnessChanges.`
+ + `Brightness: ${this.LightBulb.Brightness}, `
+ + `BrightnessCached: ${this.accessory.context.Brightness}`);
}
}
@@ -770,13 +658,13 @@ export class CeilingLight {
* Handle requests to set the value of the "On" characteristic
*/
async OnSet(value: CharacteristicValue): Promise {
- if (this.On === this.accessory.context.On) {
+ if (this.LightBulb.On === this.accessory.context.On) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set On: ${value}`);
} else {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`);
}
- this.On = value;
+ this.LightBulb.On = value;
this.doCeilingLightUpdate.next();
}
@@ -784,15 +672,15 @@ export class CeilingLight {
* Handle requests to set the value of the "Brightness" characteristic
*/
async BrightnessSet(value: CharacteristicValue): Promise {
- if (this.Brightness === this.accessory.context.Brightness) {
+ if (this.LightBulb.Brightness === this.accessory.context.Brightness) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Brightness: ${value}`);
- } else if (this.On) {
+ } else if (this.LightBulb.On) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`);
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`);
}
- this.Brightness = value;
+ this.LightBulb.Brightness = value;
this.doCeilingLightUpdate.next();
}
@@ -800,30 +688,32 @@ export class CeilingLight {
* Handle requests to set the value of the "ColorTemperature" characteristic
*/
async ColorTemperatureSet(value: CharacteristicValue): Promise {
- if (this.ColorTemperature === this.accessory.context.ColorTemperature) {
+ if (this.LightBulb.ColorTemperature === this.accessory.context.ColorTemperature) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set ColorTemperature: ${value}`);
- } else if (this.On) {
+ } else if (this.LightBulb.On) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ColorTemperature: ${value}`);
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ColorTemperature: ${value}`);
}
+ const minKelvin = 2000;
+ const maxKelvin = 9000;
// Convert mired to kelvin to nearest 100 (SwitchBot seems to need this)
const kelvin = Math.round(1000000 / Number(value) / 100) * 100;
// Check and increase/decrease kelvin to range of device
- const k = Math.min(Math.max(kelvin, this.minKelvin), this.maxKelvin);
+ const k = Math.min(Math.max(kelvin, minKelvin), maxKelvin);
- if (!this.accessory.context.On || this.cacheKelvin === k) {
+ if (!this.accessory.context.On || this.accessory.context.kelvin === k) {
return;
}
// Updating the hue/sat to the corresponding values mimics native adaptive lighting
const hs = m2hs(value);
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, hs[0]);
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, hs[1]);
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Hue, hs[0]);
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Saturation, hs[1]);
- this.ColorTemperature = value;
+ this.LightBulb.ColorTemperature = value;
this.doCeilingLightUpdate.next();
}
@@ -831,17 +721,17 @@ export class CeilingLight {
* Handle requests to set the value of the "Hue" characteristic
*/
async HueSet(value: CharacteristicValue): Promise {
- if (this.Hue === this.accessory.context.Hue) {
+ if (this.LightBulb.Hue === this.accessory.context.Hue) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Hue: ${value}`);
- } else if (this.On) {
+ } else if (this.LightBulb.On) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Hue: ${value}`);
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Hue: ${value}`);
}
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140);
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140);
- this.Hue = value;
+ this.LightBulb.Hue = value;
this.doCeilingLightUpdate.next();
}
@@ -849,55 +739,56 @@ export class CeilingLight {
* Handle requests to set the value of the "Saturation" characteristic
*/
async SaturationSet(value: CharacteristicValue): Promise {
- if (this.Saturation === this.accessory.context.Saturation) {
+ if (this.LightBulb.Saturation === this.accessory.context.Saturation) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Saturation: ${value}`);
- } else if (this.On) {
+ } else if (this.LightBulb.On) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Saturation: ${value}`);
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Saturation: ${value}`);
}
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140);
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140);
- this.Saturation = value;
+ this.LightBulb.Saturation = value;
this.doCeilingLightUpdate.next();
}
async updateHomeKitCharacteristics(): Promise {
- if (this.On === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ if (this.LightBulb.On === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`);
} else {
- this.accessory.context.On = this.On;
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.On, this.On);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
+ this.accessory.context.On = this.LightBulb.On;
+ this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.On, this.LightBulb.On);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.LightBulb.On}`);
}
- if (this.Brightness === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.Brightness}`);
+ if (this.LightBulb.Brightness === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`);
} else {
- this.accessory.context.Brightness = this.Brightness;
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Brightness, this.Brightness);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.Brightness}`);
+ this.accessory.context.Brightness = this.LightBulb.Brightness;
+ this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Brightness, this.LightBulb.Brightness);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.LightBulb.Brightness}`);
}
- if (this.ColorTemperature === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`);
+ if (this.LightBulb.ColorTemperature === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`);
} else {
- this.accessory.context.ColorTemperature = this.ColorTemperature;
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, this.ColorTemperature);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic ColorTemperature: ${this.ColorTemperature}`);
+ this.accessory.context.ColorTemperature = this.LightBulb.ColorTemperature;
+ this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, this.LightBulb.ColorTemperature);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` updateCharacteristic ColorTemperature: ${this.LightBulb.ColorTemperature}`);
}
- if (this.Hue === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`);
+ if (this.LightBulb.Hue === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`);
} else {
- this.accessory.context.Hue = this.Hue;
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, this.Hue);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Hue: ${this.Hue}`);
+ this.accessory.context.Hue = this.LightBulb.Hue;
+ this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Hue, this.LightBulb.Hue);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Hue: ${this.LightBulb.Hue}`);
}
- if (this.Saturation === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`);
+ if (this.LightBulb.Saturation === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`);
} else {
- this.accessory.context.Saturation = this.Saturation;
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, this.Saturation);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Saturation: ${this.Saturation}`);
+ this.accessory.context.Saturation = this.LightBulb.Saturation;
+ this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Saturation, this.LightBulb.Saturation);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Saturation: ${this.LightBulb.Saturation}`);
}
}
@@ -911,35 +802,6 @@ export class CeilingLight {
}
}
- async stopScanning(switchbot: any) {
- switchbot.stopScan();
- if (this.BLE_IsConnected) {
- await this.BLEparseStatus();
- await this.updateHomeKitCharacteristics();
- } else {
- await this.BLERefreshConnection(switchbot);
- }
- }
-
- async getCustomBLEAddress(switchbot: any) {
- if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} customBLEaddress: ${this.device.customBLEaddress}`);
- (async () => {
- // Start to monitor advertisement packets
- await switchbot.startScan({
- model: 'u',
- });
- // Set an event handler
- switchbot.onadvertisement = (ad: any) => {
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
- };
- await sleep(10000);
- // Stop to monitor
- switchbot.stopScan();
- })();
- }
- }
-
async BLEPushConnection() {
if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`);
@@ -956,284 +818,17 @@ export class CeilingLight {
}
}
- async retry({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise {
- return fn().catch(async (e: any) => {
- if (max === 0) {
- throw e;
- }
- this.infoLog(e);
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`);
- await sleep(1000);
- return this.retry({ max: max - 1, fn });
- });
- }
-
- maxRetry(): number {
- if (this.device.maxRetry) {
- return this.device.maxRetry;
- } else {
- return 5;
- }
- }
-
- minStep(device: device & devicesConfig): number {
- if (device.ceilinglight?.set_minStep) {
- this.set_minStep = device.ceilinglight?.set_minStep;
- } else {
- this.set_minStep = 1;
- }
- return this.set_minStep;
- }
-
- async scan(device: device & devicesConfig): Promise {
- if (device.scanDuration) {
- this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
- }
- } else {
- this.scanDuration = this.accessory.context.scanDuration = 1;
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
- }
- }
- }
-
- async statusCode(statusCode: number): Promise {
- switch (statusCode) {
- case 151:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
- break;
- case 152:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
- break;
- case 160:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
- break;
- case 161:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
- this.offlineOff();
- break;
- case 171:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` +
- `Hub: ${this.device.hubDeviceId}`,
- );
- this.offlineOff();
- break;
- case 190:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
- ` Or command format is invalid, statusCode: ${statusCode}`,
- );
- break;
- case 100:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
- break;
- case 200:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
- break;
- case 400:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. `
- + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`);
- break;
- case 401:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, `
- + `but the request has not been authenticated, statusCode: ${statusCode}`);
- break;
- case 403:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not `
- + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`);
- break;
- case 404:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, `
- + `statusCode: ${statusCode}`);
- break;
- case 406:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via `
- + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`);
- break;
- case 415:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType `
- + `header that is not supported by the server, statusCode: ${statusCode}`);
- break;
- case 422:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, The client has made a valid request, `
- + `but the server cannot process it. This is often used for APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
- break;
- case 429:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of `
- + `requests allowed for a given time window, statusCode: ${statusCode}`);
- break;
- case 500:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings `
- + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`);
- break;
- default:
- this.infoLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` +
- `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`,
- );
- }
- }
-
async offlineOff(): Promise {
if (this.device.offline) {
- await this.deviceContext();
- await this.updateHomeKitCharacteristics();
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, false);
}
}
apiError(e: any): void {
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.On, e);
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, e);
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Brightness, e);
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, e);
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, e);
- }
-
- async deviceContext() {
- if (this.On === undefined) {
- this.On = false;
- } else {
- this.On = this.accessory.context.On;
- }
- if (this.Hue === undefined) {
- this.Hue = 0;
- } else {
- this.Hue = this.accessory.context.Hue;
- }
- if (this.Brightness === undefined) {
- this.Brightness = 0;
- } else {
- this.Brightness = this.accessory.context.Brightness;
- }
- if (this.Brightness === undefined) {
- this.Saturation = 0;
- } else {
- this.Saturation = this.accessory.context.Saturation;
- }
- if (this.ColorTemperature === undefined) {
- this.ColorTemperature = 140;
- } else {
- this.ColorTemperature = this.accessory.context.ColorTemperature;
- }
- this.minKelvin = 2000;
- this.maxKelvin = 9000;
- if (this.FirmwareRevision === undefined) {
- this.FirmwareRevision = this.platform.version;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
- }
- }
-
- async refreshRate(device: device & devicesConfig): Promise {
- if (device.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
- } else if (this.platform.config.options!.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
- }
- }
-
- async deviceConfig(device: device & devicesConfig): Promise {
- let config = {};
- if (device.ceilinglight) {
- config = device.ceilinglight;
- }
- if (device.connectionType !== undefined) {
- config['connectionType'] = device.connectionType;
- }
- if (device.external !== undefined) {
- config['external'] = device.external;
- }
- if (device.logging !== undefined) {
- config['logging'] = device.logging;
- }
- if (device.refreshRate !== undefined) {
- config['refreshRate'] = device.refreshRate;
- }
- if (device.scanDuration !== undefined) {
- config['scanDuration'] = device.scanDuration;
- }
- if (device.offline !== undefined) {
- config['offline'] = device.offline;
- }
- if (device.maxRetry !== undefined) {
- config['maxRetry'] = device.maxRetry;
- }
- if (device.webhook !== undefined) {
- config['webhook'] = device.webhook;
- }
- if (Object.entries(config).length !== 0) {
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
- }
- }
-
- async deviceLogs(device: device & devicesConfig): Promise {
- if (this.platform.debugMode) {
- this.deviceLogging = this.accessory.context.logging = 'debugMode';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
- } else if (device.logging) {
- this.deviceLogging = this.accessory.context.logging = device.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
- } else if (this.platform.config.options?.logging) {
- this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
- } else {
- this.deviceLogging = this.accessory.context.logging = 'standard';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
- }
- }
-
- /**
- * Logging for Device
- */
- infoLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.info(String(...log));
- }
- }
-
- warnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.warn(String(...log));
- }
- }
-
- debugWarnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.warn('[DEBUG]', String(...log));
- }
- }
- }
-
- errorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.error(String(...log));
- }
- }
-
- debugErrorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.error('[DEBUG]', String(...log));
- }
- }
- }
-
- debugLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging === 'debug') {
- this.platform.log.info('[DEBUG]', String(...log));
- } else {
- this.platform.log.debug(String(...log));
- }
- }
- }
-
- enablingDeviceLogging(): boolean {
- return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard';
+ this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.On, e);
+ this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Hue, e);
+ this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Brightness, e);
+ this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.Saturation, e);
+ this.LightBulb!.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, e);
}
}
diff --git a/src/device/colorbulb.ts b/src/device/colorbulb.ts
index fce61c14..7bf406c7 100644
--- a/src/device/colorbulb.ts
+++ b/src/device/colorbulb.ts
@@ -1,152 +1,110 @@
+/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+ *
+ * blindtilt.ts: @switchbot/homebridge-switchbot.
+ */
import { request } from 'undici';
-import { sleep } from '../utils.js';
+import { deviceBase } from './device.js';
import { interval, Subject } from 'rxjs';
-import { SwitchBotPlatform } from '../platform.js';
+import { Devices } from '../settings.js';
+import { hs2rgb, rgb2hs, m2hs } from '../utils.js';
import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
-import { device, devicesConfig, deviceStatus, hs2rgb, rgb2hs, m2hs, serviceData, Devices, SwitchBotPlatformConfig } from '../settings.js';
-import {
- Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap, API, Logging, HAP,
-} from 'homebridge';
+
+import type { SwitchBotPlatform } from '../platform.js';
+import type { device, devicesConfig, deviceStatus, serviceData } from '../settings.js';
+import type { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge';
/**
* Platform Accessory
* An instance of this class is created for each accessory your platform registers
* Each accessory may expose multiple services of different service types.
*/
-export class ColorBulb {
- public readonly api: API;
- public readonly log: Logging;
- public readonly config!: SwitchBotPlatformConfig;
- protected readonly hap: HAP;
+export class ColorBulb extends deviceBase {
// Services
- lightBulbService!: Service;
-
- // Characteristic Values
- On!: CharacteristicValue;
- Hue!: CharacteristicValue;
- Saturation!: CharacteristicValue;
- Brightness!: CharacteristicValue;
- FirmwareRevision!: CharacteristicValue;
- ColorTemperature?: CharacteristicValue;
-
- // OpenAPI Status
- OpenAPI_On: deviceStatus['power'];
- OpenAPI_RGB: deviceStatus['color'];
- OpenAPI_Brightness: deviceStatus['brightness'];
- OpenAPI_FirmwareRevision: deviceStatus['version'];
- OpenAPI_ColorTemperature?: deviceStatus['colorTemperature'];
-
- // BLE Status
- BLE_ColorTemperature: serviceData['color_temperature'];
- BLE_Power: serviceData['power'];
- BLE_Red: serviceData['red'];
- BLE_Blue: serviceData['blue'];
- BLE_Green: serviceData['green'];
- BLE_On: serviceData['state'];
- BLE_Delay: serviceData['delay'];
- BLE_WifiRssi: serviceData['wifiRssi'];
- BLE_Brightness: serviceData['brightness'];
- BLE_Saturation;
- BLE_Hue;
-
- // BLE Others
- BLE_IsConnected?: boolean;
-
- // Config
- set_minStep?: number;
- scanDuration!: number;
- deviceLogging!: string;
- deviceRefreshRate!: number;
- adaptiveLightingShift?: number;
+ private LightBulb: {
+ Name: CharacteristicValue;
+ Service: Service;
+ On: CharacteristicValue;
+ Hue: CharacteristicValue;
+ Saturation: CharacteristicValue;
+ Brightness: CharacteristicValue;
+ ColorTemperature?: CharacteristicValue;
+ };
// Adaptive Lighting
AdaptiveLightingController?: ControllerConstructor | Controller;
- minKelvin!: number;
- maxKelvin!: number;
-
- // Others
- cacheKelvin!: number;
+ adaptiveLightingShift?: number;
// Updates
colorBulbUpdateInProgress!: boolean;
doColorBulbUpdate!: Subject;
- // Connection
- private readonly OpenAPI: boolean;
- private readonly BLE: boolean;
-
constructor(
- private readonly platform: SwitchBotPlatform,
- private accessory: PlatformAccessory,
- public device: device & devicesConfig,
+ readonly platform: SwitchBotPlatform,
+ accessory: PlatformAccessory,
+ device: device & devicesConfig,
) {
- this.api = this.platform.api;
- this.log = this.platform.log;
- this.config = this.platform.config;
- this.hap = this.api.hap;
- // Connection
- this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI';
- this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI';
+ super(platform, accessory, device);
// default placeholders
- this.deviceLogs(device);
- this.scan(device);
- this.refreshRate(device);
this.adaptiveLighting(device);
- this.deviceContext();
- this.deviceConfig(device);
// this is subject we use to track when we need to POST changes to the SwitchBot API
this.doColorBulbUpdate = new Subject();
this.colorBulbUpdateInProgress = false;
- // Retrieve initial values and updateHomekit
- this.refreshStatus();
-
- // set accessory information
- accessory
- .getService(this.hap.Service.AccessoryInformation)!
- .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
- .setCharacteristic(this.hap.Characteristic.Model, 'W1401400')
- .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId)
- .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
-
- // get the Lightbulb service if it exists, otherwise create a new Lightbulb service
- // you can create multiple services for each accessory
- const lightBulbService = `${accessory.displayName} ${device.deviceType}`;
- (this.lightBulbService = accessory.getService(this.hap.Service.Lightbulb)
- || accessory.addService(this.hap.Service.Lightbulb)), lightBulbService;
-
- if (this.adaptiveLightingShift === -1 && this.accessory.context.adaptiveLighting) {
- this.accessory.removeService(this.lightBulbService);
- this.lightBulbService = this.accessory.addService(this.hap.Service.Lightbulb);
- this.accessory.context.adaptiveLighting = false;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting}`);
+ // Initialize LightBulb property
+ accessory.context.LightBulb = accessory.context.LightBulb ?? {};
+ this.LightBulb = {
+ Name: accessory.context.LightBulb.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service,
+ On: accessory.context.On ?? false,
+ Hue: accessory.context.Hue ?? 0,
+ Saturation: accessory.context.Saturation ?? 0,
+ Brightness: accessory.context.Brightness ?? 0,
+ ColorTemperature: accessory.context.ColorTemperature ?? 140,
+ };
+ accessory.context.LightBulb = this.LightBulb as object;
+
+ // Adaptive Lighting
+ if (this.adaptiveLightingShift === -1 && accessory.context.adaptiveLighting) {
+ accessory.removeService(this.LightBulb.Service);
+ this.LightBulb.Service = accessory.addService(this.hap.Service.Lightbulb);
+ accessory.context.adaptiveLighting = false;
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} adaptiveLighting: ${accessory.context.adaptiveLighting}`);
}
-
- this.lightBulbService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.lightBulbService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.lightBulbService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
+ if (this.adaptiveLightingShift !== -1) {
+ this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.LightBulb.Service, {
+ customTemperatureAdjustment: this.adaptiveLightingShift,
+ });
+ accessory.configureController(this.AdaptiveLightingController);
+ accessory.context.adaptiveLighting = true;
+ this.debugLog(`${device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${accessory.context.adaptiveLighting},`
+ + ` adaptiveLightingShift: ${this.adaptiveLightingShift}`);
}
+ this.debugLog(`${device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`);
- // handle on / off events using the On characteristic
- this.lightBulbService.getCharacteristic(this.hap.Characteristic.On).onSet(this.OnSet.bind(this));
+ // Initialize LightBulb Characteristics
+ this.LightBulb.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.LightBulb.Name)
+ .getCharacteristic(this.hap.Characteristic.On)
+ .onGet(() => {
+ return this.LightBulb.On;
+ })
+ .onSet(this.OnSet.bind(this));
- // handle Brightness events using the Brightness characteristic
- this.lightBulbService
+ this.LightBulb.Service
.getCharacteristic(this.hap.Characteristic.Brightness)
.setProps({
- minStep: this.minStep(device),
+ minStep: device.colorbulb?.set_minStep ?? 1,
minValue: 0,
maxValue: 100,
validValueRanges: [0, 100],
})
.onGet(() => {
- return this.Brightness;
+ return this.LightBulb.Brightness;
})
.onSet(this.BrightnessSet.bind(this));
- // handle ColorTemperature events using the ColorTemperature characteristic
- this.lightBulbService
+ this.LightBulb.Service
.getCharacteristic(this.hap.Characteristic.ColorTemperature)
.setProps({
minValue: 140,
@@ -154,12 +112,11 @@ export class ColorBulb {
validValueRanges: [140, 500],
})
.onGet(() => {
- return this.ColorTemperature!;
+ return this.LightBulb.ColorTemperature!;
})
.onSet(this.ColorTemperatureSet.bind(this));
- // handle Hue events using the Hue characteristic
- this.lightBulbService
+ this.LightBulb.Service
.getCharacteristic(this.hap.Characteristic.Hue)
.setProps({
minValue: 0,
@@ -167,12 +124,11 @@ export class ColorBulb {
validValueRanges: [0, 360],
})
.onGet(() => {
- return this.Hue;
+ return this.LightBulb.Hue;
})
.onSet(this.HueSet.bind(this));
- // handle Hue events using the Hue characteristic
- this.lightBulbService
+ this.LightBulb.Service
.getCharacteristic(this.hap.Characteristic.Saturation)
.setProps({
minValue: 0,
@@ -180,22 +136,12 @@ export class ColorBulb {
validValueRanges: [0, 100],
})
.onGet(() => {
- return this.Saturation;
+ return this.LightBulb.Saturation;
})
.onSet(this.SaturationSet.bind(this));
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} adaptiveLightingShift: ${this.adaptiveLightingShift}`);
- if (this.adaptiveLightingShift !== -1) {
- this.AdaptiveLightingController = new platform.api.hap.AdaptiveLightingController(this.lightBulbService, {
- customTemperatureAdjustment: this.adaptiveLightingShift,
- });
- this.accessory.configureController(this.AdaptiveLightingController);
- this.accessory.context.adaptiveLighting = true;
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} adaptiveLighting: ${this.accessory.context.adaptiveLighting},` +
- ` adaptiveLightingShift: ${this.adaptiveLightingShift}`,
- );
- }
+ // Retrieve initial values and updateHomekit
+ this.refreshStatus();
// Update Homekit
this.updateHomeKitCharacteristics();
@@ -208,60 +154,7 @@ export class ColorBulb {
});
//regisiter webhook event handler
- if (this.device.webhook) {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`);
- this.platform.webhookEventHandler[this.device.deviceId] = async (context) => {
- try {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
- const { powerState, brightness, color, colorTemperature } = context;
- const { On, Brightness, Hue, Saturation, ColorTemperature } = this;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` +
- '(powerState, brightness, color, colorTemperature) = ' +
- `Webhook:(${powerState}, ${brightness}, ${color}, ${colorTemperature}), ` +
- `current:(${On}, ${Brightness}, ${Hue}, ${Saturation}, ${ColorTemperature})`);
- this.On = powerState === 'ON' ? true : false;
- this.Brightness = brightness;
-
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(color)}`);
- const [red, green, blue] = color!.split(':');
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} red: ${JSON.stringify(red)}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} green: ${JSON.stringify(green)}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${JSON.stringify(blue)}`);
-
- const [hue, saturation] = rgb2hs(Number(red), Number(green), Number(blue));
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`,
- );
-
- // Hue
- this.Hue = hue;
- if (this.accessory.context.Hue !== this.Hue) {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`);
- } else {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`);
- }
-
- // Saturation
- this.Saturation = saturation;
- if (this.accessory.context.Saturation !== this.Saturation) {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`);
- } else {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`);
- }
-
- this.ColorTemperature = colorTemperature;
- if (this.accessory.context.ColorTemperature !== this.ColorTemperature) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`);
- } else {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`);
- }
- this.updateHomeKitCharacteristics();
- } catch (e: any) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
- + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
- }
- };
- }
+ this.registerWebhook(accessory, device);
// Watch for Bulb change events
// We put in a debounce of 100ms so we don't make duplicate calls
@@ -270,138 +163,130 @@ export class ColorBulb {
tap(() => {
this.colorBulbUpdateInProgress = true;
}),
- debounceTime(this.platform.config.options!.pushRate! * 1000),
+ debounceTime(this.devicePushRate * 1000),
)
.subscribe(async () => {
try {
await this.pushChanges();
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` +
- ` Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,`
+ + ` Error Message: ${JSON.stringify(e.message)}`);
}
this.colorBulbUpdateInProgress = false;
});
}
/**
- * Parse the device status from the SwitchBot api
+ * Parse the device status from the SwitchBotBLE API
*/
- async parseStatus(): Promise {
- if (!this.device.enableCloudService && this.OpenAPI) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`);
- } else if (this.BLE) {
- await this.BLEparseStatus();
- } else if (this.OpenAPI && this.platform.config.credentials?.token) {
- await this.openAPIparseStatus();
- } else {
- await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`,
- );
- }
- }
-
- async BLEparseStatus(): Promise {
+ async BLEparseStatus(serviceData: serviceData): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
// State
- switch (this.BLE_On) {
+ switch (serviceData.power) {
case true:
- this.On = true;
+ this.LightBulb.On = true;
break;
default:
- this.On = false;
+ this.LightBulb.On = false;
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`);
// Brightness
- this.Brightness = Number(this.BLE_Brightness);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.Brightness}`);
+ this.LightBulb.Brightness = Number(serviceData.brightness);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`);
// Color, Hue & Brightness
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} red: ${this.BLE_Red}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} green: ${this.BLE_Green}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${this.BLE_Blue}`);
-
- const [hue, saturation] = rgb2hs(Number(this.BLE_Red), Number(this.BLE_Green), Number(this.BLE_Blue));
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` +
- ` hs: ${JSON.stringify(rgb2hs(Number(this.BLE_Red), Number(this.BLE_Green), Number(this.BLE_Blue)))}`,
- );
- this.BLE_Hue = hue;
- this.BLE_Saturation = saturation;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} red: ${serviceData.red}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} green: ${serviceData.green}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${serviceData.blue}`);
+
+ const [hue, saturation] = rgb2hs(Number(serviceData.red), Number(serviceData.green), Number(serviceData.blue));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` hs: ${JSON.stringify(rgb2hs(Number(serviceData.red), Number(serviceData.green), Number(serviceData.blue)))}`);
// Hue
- this.Hue = this.BLE_Hue;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`);
+ this.LightBulb.Hue = hue;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`);
// Saturation
- this.Saturation = this.BLE_Saturation;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`);
+ this.LightBulb.Saturation = saturation;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`);
// ColorTemperature
- if (this.BLE_ColorTemperature) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.BLE_ColorTemperature}`);
- this.ColorTemperature = this.BLE_ColorTemperature!;
+ if (serviceData.color_temperature) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${serviceData.color_temperature}`);
+ this.LightBulb.ColorTemperature = serviceData.color_temperature!;
- this.ColorTemperature = Math.max(Math.min(this.ColorTemperature, 500), 140);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`);
+ this.LightBulb.ColorTemperature = Math.max(Math.min(this.LightBulb.ColorTemperature, 500), 140);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`);
}
}
- async openAPIparseStatus() {
+ /**
+ * Parse the device status from the SwitchBot OpenAPI
+ */
+ async openAPIparseStatus(deviceStatus: deviceStatus): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
- switch (this.OpenAPI_On) {
+ switch (deviceStatus.body.power) {
case 'on':
- this.On = true;
+ this.LightBulb.On = true;
break;
default:
- this.On = false;
+ this.LightBulb.On = false;
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`);
// Brightness
- this.Brightness = Number(this.OpenAPI_Brightness);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.Brightness}`);
+ this.LightBulb.Brightness = Number(deviceStatus.body.brightness);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`);
// Color, Hue & Brightness
- if (this.OpenAPI_RGB) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(this.OpenAPI_RGB)}`);
- const [red, green, blue] = this.OpenAPI_RGB!.split(':');
+ if (deviceStatus.body.color) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} color: ${JSON.stringify(deviceStatus.body.color)}`);
+ const [red, green, blue] = deviceStatus.body.color!.split(':');
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} red: ${JSON.stringify(red)}`);
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} green: ${JSON.stringify(green)}`);
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} blue: ${JSON.stringify(blue)}`);
const [hue, saturation] = rgb2hs(Number(red), Number(green), Number(blue));
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`);
// Hue
- this.Hue = hue;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`);
+ this.LightBulb.Hue = hue;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`);
// Saturation
- this.Saturation = saturation;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`);
+ this.LightBulb.Saturation = saturation;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`);
}
// ColorTemperature
- if (!Number.isNaN(this.OpenAPI_ColorTemperature)) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.OpenAPI_ColorTemperature}`);
- const mired = Math.round(1000000 / this.OpenAPI_ColorTemperature!);
+ if (!Number.isNaN(deviceStatus.body.colorTemperature)) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${deviceStatus.body.colorTemperature}`);
+ const mired = Math.round(1000000 / deviceStatus.body.colorTemperature!);
- this.ColorTemperature = Number(mired);
+ this.LightBulb.ColorTemperature = Number(mired);
- this.ColorTemperature = Math.max(Math.min(this.ColorTemperature, 500), 140);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`);
+ this.LightBulb.ColorTemperature = Math.max(Math.min(this.LightBulb.ColorTemperature, 500), 140);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`);
}
- // FirmwareRevision
- this.FirmwareRevision = this.OpenAPI_FirmwareRevision!;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
+ // Firmware Version
+ const version = deviceStatus.body.version?.toString();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
+ if (deviceStatus.body.version) {
+ const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
+ this.accessory
+ .getService(this.hap.Service.AccessoryInformation)!
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
+ .updateValue(deviceVersion);
+ this.accessory.context.deviceVersion = deviceVersion;
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
+ }
}
/**
@@ -416,10 +301,8 @@ export class ColorBulb {
await this.openAPIRefreshStatus();
} else {
await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` +
- ` ${this.device.connectionType}, refreshStatus will not happen.`,
- );
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
}
}
@@ -436,113 +319,43 @@ export class ColorBulb {
// Start to monitor advertisement packets
(async () => {
// Start to monitor advertisement packets
- await switchbot.startScan({
- model: 'u',
- id: this.device.bleMac,
- });
+ await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac });
// Set an event handler
switchbot.onadvertisement = (ad: any) => {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`);
- if (this.device.bleMac === ad.address && ad.serviceData.model === 'u') {
+ if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.BLE_ColorTemperature = ad.serviceData.color_temperature;
- this.BLE_Power = ad.serviceData.power;
- this.BLE_On = ad.serviceData.state;
- this.BLE_Red = ad.serviceData.red;
- this.BLE_Green = ad.serviceData.green;
- this.BLE_Blue = ad.serviceData.blue;
- this.BLE_Brightness = ad.serviceData.brightness;
- this.BLE_Delay = ad.serviceData.delay;
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
}
};
- // Wait 1 seconds
+ // Wait 10 seconds
await switchbot.wait(this.scanDuration * 1000);
// Stop to monitor
await switchbot.stopScan();
// Update HomeKit
- await this.BLEparseStatus();
+ await this.BLEparseStatus(switchbot.onadvertisement.serviceData);
await this.updateHomeKitCharacteristics();
})();
- /*if (switchbot !== false) {
- switchbot
- .startScan({
- model: 'u',
- id: this.device.bleMac,
- })
- .then(async () => {
- // Set an event handler
- switchbot.onadvertisement = async (ad: ad) => {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` +
- ` BLE Address Found: ${ad.address}`,
- );
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.BLE_Power = ad.serviceData.power;
- this.BLE_On = ad.serviceData.state;
- this.BLE_Red = ad.serviceData.red;
- this.BLE_Green = ad.serviceData.green;
- this.BLE_Blue = ad.serviceData.blue;
- this.BLE_ColorTemperature = ad.serviceData.color_temperature;
- this.BLE_Brightness = ad.serviceData.brightness;
- this.BLE_Delay = ad.serviceData.delay;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} state: ${ad.serviceData.state}, ` +
- `delay: ${ad.serviceData.delay}, timer: ${ad.serviceData.timer}, syncUtcTime: ${ad.serviceData.syncUtcTime} ` +
- `wifiRssi: ${ad.serviceData.wifiRssi}, overload: ${ad.serviceData.overload}, currentPower: ${ad.serviceData.currentPower}`,
- );
-
- if (ad.serviceData) {
- this.BLE_IsConnected = true;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
- await this.stopScanning(switchbot);
- } else {
- this.BLE_IsConnected = false;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
- }
- };
- // Wait
- return await sleep(this.scanDuration * 1000);
- })
- .then(async () => {
- // Stop to monitor
- await this.stopScanning(switchbot);
- })
- .catch(async (e: any) => {
- this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
- await this.BLERefreshConnection(switchbot);
- });
- } else {
+ if (switchbot === undefined) {
await this.BLERefreshConnection(switchbot);
- }*/
+ }
}
- async openAPIRefreshStatus() {
+ async openAPIRefreshStatus(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
try {
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, {
- headers: this.platform.generateHeaders(),
- });
+ const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries,
+ `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() });
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
const deviceStatus: any = await body.json();
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
- this.OpenAPI_On = deviceStatus.body.power;
- this.OpenAPI_RGB = deviceStatus.body.color;
- this.OpenAPI_Brightness = deviceStatus.body.brightness;
- this.OpenAPI_ColorTemperature = deviceStatus.body.colorTemperature;
- this.OpenAPI_FirmwareRevision = deviceStatus.body.version;
- this.openAPIparseStatus();
+ this.openAPIparseStatus(deviceStatus);
this.updateHomeKitCharacteristics();
} else {
this.statusCode(statusCode);
@@ -550,10 +363,81 @@ export class ColorBulb {
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
+ }
+ }
+
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
+ if (device.webhook) {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`);
+ this.platform.webhookEventHandler[device.deviceId] = async (context) => {
+ try {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
+ const { powerState, brightness, color, colorTemperature } = context;
+ const { On, Brightness, Hue, Saturation, ColorTemperature } = this.LightBulb;
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} ` +
+ '(powerState, brightness, color, colorTemperature) = ' +
+ `Webhook:(${powerState}, ${brightness}, ${color}, ${colorTemperature}), `
+ + `current:(${On}, ${Brightness}, ${Hue}, ${Saturation}, ${ColorTemperature})`);
+
+ // On
+ this.LightBulb.On = powerState === 'ON' ? true : false;
+ if (accessory.context.Brightness !== this.LightBulb.On) {
+ this.infoLog(`${device.deviceType}: ${accessory.displayName} On: ${this.LightBulb.On}`);
+ } else {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} On: ${this.LightBulb.On}`);
+ }
+
+ // Brightness
+ this.LightBulb.Brightness = brightness;
+ if (accessory.context.Brightness !== this.LightBulb.Brightness) {
+ this.infoLog(`${device.deviceType}: ${accessory.displayName} Brightness: ${this.LightBulb.Brightness}`);
+ } else {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} Brightness: ${this.LightBulb.Brightness}`);
+ }
+
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} color: ${JSON.stringify(color)}`);
+ const [red, green, blue] = color!.split(':');
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} red: ${JSON.stringify(red)}`);
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} green: ${JSON.stringify(green)}`);
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} blue: ${JSON.stringify(blue)}`);
+
+ const [hue, saturation] = rgb2hs(Number(red), Number(green), Number(blue));
+ this.debugLog(
+ `${device.deviceType}: ${accessory.displayName}`
+ + ` hs: ${JSON.stringify(rgb2hs(Number(red), Number(green), Number(blue)))}`);
+
+ // Hue
+ this.LightBulb.Hue = hue;
+ if (accessory.context.Hue !== this.LightBulb.Hue) {
+ this.infoLog(`${device.deviceType}: ${accessory.displayName} Hue: ${this.LightBulb.Hue}`);
+ } else {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} Hue: ${this.LightBulb.Hue}`);
+ }
+
+ // Saturation
+ this.LightBulb.Saturation = saturation;
+ if (accessory.context.Saturation !== this.LightBulb.Saturation) {
+ this.infoLog(`${device.deviceType}: ${accessory.displayName} Saturation: ${this.LightBulb.Saturation}`);
+ } else {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} Saturation: ${this.LightBulb.Saturation}`);
+ }
+
+ this.LightBulb.ColorTemperature = colorTemperature;
+ if (accessory.context.ColorTemperature !== this.LightBulb.ColorTemperature) {
+ this.infoLog(`${device.deviceType}: ${accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`);
+ } else {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`);
+ }
+ this.updateHomeKitCharacteristics();
+ } catch (e: any) {
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} `
+ + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
+ }
+ };
+ } else {
+ this.debugWarnLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`);
}
}
@@ -571,15 +455,14 @@ export class ColorBulb {
async pushChanges(): Promise {
if (!this.device.enableCloudService && this.OpenAPI) {
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} pushChanges enableCloudService: ${this.device.enableCloudService}`);
- /*} else if (this.BLE) {
- await this.BLEpushChanges();*/
+ } else if (this.BLE) {
+ await this.BLEpushChanges();
} else if (this.OpenAPI && this.platform.config.credentials?.token) {
await this.openAPIpushChanges();
} else {
await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`,
- );
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, pushChanges will not happen.`);
}
// Refresh the status from the API
interval(15000)
@@ -592,8 +475,9 @@ export class ColorBulb {
async BLEpushChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`);
- if (this.On !== this.accessory.context.On) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges On: ${this.On} OnCached: ${this.accessory.context.On}`);
+ if (this.LightBulb.On !== this.accessory.context.On) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`
+ + ` On: ${this.LightBulb.On}, OnCached: ${this.accessory.context.On}`);
const switchbot = await this.platform.connectBLE();
// Convert to BLE Address
this.device.bleMac = this.device
@@ -607,11 +491,11 @@ export class ColorBulb {
id: this.device.bleMac,
})
.then(async (device_list: any) => {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
- return await this.retry({
- max: this.maxRetry(),
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`);
+ return await this.retryBLE({
+ max: await this.maxRetryBLE(),
fn: async () => {
- if (this.On) {
+ if (this.LightBulb.On) {
return await device_list[0].turnOn({ id: this.device.bleMac });
} else {
return await device_list[0].turnOff({ id: this.device.bleMac });
@@ -621,38 +505,37 @@ export class ColorBulb {
})
.then(() => {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
- this.On = false;
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `On: ${this.LightBulb.On} sent over BLE, sent successfully`);
+ this.LightBulb.On = false;
})
.catch(async (e: any) => {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
await this.BLEPushConnection();
});
// Push Brightness Update
- if (this.On) {
+ if (this.LightBulb.On) {
await this.BLEpushBrightnessChanges();
}
// Push ColorTemperature Update
- if (this.On) {
+ if (this.LightBulb.On) {
await this.BLEpushColorTemperatureChanges();
}
// Push Hue & Saturation Update
- if (this.On) {
+ if (this.LightBulb.On) {
await this.BLEpushRGBChanges();
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges.` + `On: ${this.On}, ` + `OnCached: ${this.accessory.context.On}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, On: ${this.LightBulb.On},`
+ + ` OnCached: ${this.accessory.context.On}`);
}
}
async BLEpushBrightnessChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushBrightnessChanges`);
- if (this.Brightness !== this.accessory.context.Brightness) {
+ if (this.LightBulb.Brightness !== this.accessory.context.Brightness) {
const switchbot = await this.platform.connectBLE();
// Convert to BLE Address
this.device.bleMac = this.device
@@ -666,33 +549,28 @@ export class ColorBulb {
id: this.device.bleMac,
})
.then(async (device_list: any) => {
- this.infoLog(`${this.accessory.displayName} Target Brightness: ${this.Brightness}`);
- return await device_list[0].setBrightness(this.Brightness);
+ this.infoLog(`${this.accessory.displayName} Target Brightness: ${this.LightBulb.Brightness}`);
+ return await device_list[0].setBrightness(this.LightBulb.Brightness);
})
.then(() => {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
- this.On = false;
+ this.LightBulb.On = false;
})
.catch(async (e: any) => {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushBrightnessChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushBrightnessChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
await this.BLEPushConnection();
});
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushBrightnessChanges.` +
- `Brightness: ${this.Brightness}, ` +
- `BrightnessCached: ${this.accessory.context.Brightness}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushBrightnessChanges.`
+ + `Brightness: ${this.LightBulb.Brightness}, BrightnessCached: ${this.accessory.context.Brightness}`);
}
}
async BLEpushColorTemperatureChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushColorTemperatureChanges`);
- if (this.ColorTemperature !== this.accessory.context.ColorTemperature) {
+ if (this.LightBulb.ColorTemperature !== this.accessory.context.ColorTemperature) {
const switchbot = await this.platform.connectBLE();
// Convert to BLE Address
this.device.bleMac = this.device
@@ -706,36 +584,32 @@ export class ColorBulb {
id: this.device.bleMac,
})
.then(async (device_list: any) => {
- this.infoLog(`${this.accessory.displayName} Target ColorTemperature: ${this.ColorTemperature}`);
- return await device_list[0].setColorTemperature(this.ColorTemperature);
+ this.infoLog(`${this.accessory.displayName} Target ColorTemperature: ${this.LightBulb.ColorTemperature}`);
+ return await device_list[0].setColorTemperature(this.LightBulb.ColorTemperature);
})
.then(() => {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
- this.On = false;
+ this.LightBulb.On = false;
})
.catch(async (e: any) => {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushColorTemperatureChanges with ` +
- `${this.device.connectionType} Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushColorTemperatureChanges with `
+ + `${this.device.connectionType} Connection, Error Message: ${JSON.stringify(e.message)}`);
await this.BLEPushConnection();
});
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushColorTemperatureChanges.` +
- `ColorTemperature: ${this.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushColorTemperatureChanges.`
+ + `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`);
}
}
async BLEpushRGBChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushRGBChanges`);
- if (this.Hue !== this.accessory.context.Hue || this.Saturation !== this.accessory.context.Saturation) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.Hue)}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.Saturation)}`);
+ if (this.LightBulb.Hue !== this.accessory.context.Hue || this.LightBulb.Saturation !== this.accessory.context.Saturation) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.LightBulb.Hue)}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.LightBulb.Saturation)}`);
- const [red, green, blue] = hs2rgb(Number(this.Hue), Number(this.Saturation));
+ const [red, green, blue] = hs2rgb(Number(this.LightBulb.Hue), Number(this.LightBulb.Saturation));
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} rgb: ${JSON.stringify([red, green, blue])}`);
const switchbot = await this.platform.connectBLE();
@@ -751,34 +625,30 @@ export class ColorBulb {
id: this.device.bleMac,
})
.then(async (device_list: any) => {
- this.infoLog(`${this.accessory.displayName} Target RGB: ${(this.Brightness, red, green, blue)}`);
- return await device_list[0].setRGB(this.Brightness, red, green, blue);
+ this.infoLog(`${this.accessory.displayName} Target RGB: ${(this.LightBulb.Brightness, red, green, blue)}`);
+ return await device_list[0].setRGB(this.LightBulb.Brightness, red, green, blue);
})
.then(() => {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
- this.On = false;
+ this.LightBulb.On = false;
})
.catch(async (e: any) => {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushRGBChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushRGBChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
await this.BLEPushConnection();
});
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushRGBChanges. Hue: ${this.Hue}, ` +
- `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushRGBChanges. Hue: ${this.LightBulb.Hue}, HueCached: `
+ + `${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`);
}
}
async openAPIpushChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`);
- if (this.On !== this.accessory.context.On) {
+ if (this.LightBulb.On !== this.accessory.context.On) {
let command = '';
- if (this.On) {
+ if (this.LightBulb.On) {
command = 'turnOn';
} else {
command = 'turnOff';
@@ -803,44 +673,41 @@ export class ColorBulb {
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
} else {
this.statusCode(statusCode);
this.statusCode(deviceStatus.statusCode);
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.` +
- `On: ${this.On}, ` +
- `OnCached: ${this.accessory.context.On}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges,`
+ + `On: ${this.LightBulb.On}, OnCached: ${this.accessory.context.On}`);
}
// Push Hue & Saturation Update
- if (this.On) {
+ if (this.LightBulb.On) {
await this.pushHueSaturationChanges();
}
// Push ColorTemperature Update
- if (this.On) {
+ if (this.LightBulb.On) {
await this.pushColorTemperatureChanges();
}
// Push Brightness Update
- if (this.On) {
+ if (this.LightBulb.On) {
await this.pushBrightnessChanges();
}
}
async pushHueSaturationChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushHueSaturationChanges`);
- if (this.Hue !== this.accessory.context.Hue || this.Saturation !== this.accessory.context.Saturation) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.Hue)}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.Saturation)}`);
- const [red, green, blue] = hs2rgb(Number(this.Hue), Number(this.Saturation));
+ if (this.LightBulb.Hue !== this.accessory.context.Hue || this.LightBulb.Saturation !== this.accessory.context.Saturation) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${JSON.stringify(this.LightBulb.Hue)}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${JSON.stringify(this.LightBulb.Saturation)}`);
+ const [red, green, blue] = hs2rgb(Number(this.LightBulb.Hue), Number(this.LightBulb.Saturation));
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} rgb: ${JSON.stringify([red, green, blue])}`);
const bodyChange = JSON.stringify({
command: 'setColor',
@@ -862,30 +729,28 @@ export class ColorBulb {
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
} else {
this.statusCode(statusCode);
this.statusCode(deviceStatus.statusCode);
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushHueSaturationChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushHueSaturationChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.Hue}, ` +
- `HueCached: ${this.accessory.context.Hue}, Saturation: ${this.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushHueSaturationChanges. Hue: ${this.LightBulb.Hue}, HueCached: `
+ + `${this.accessory.context.Hue}, Saturation: ${this.LightBulb.Saturation}, SaturationCached: ${this.accessory.context.Saturation}`);
}
}
async pushColorTemperatureChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushColorTemperatureChanges`);
- if (this.ColorTemperature !== this.accessory.context.ColorTemperature) {
- const kelvin = Math.round(1000000 / Number(this.ColorTemperature));
- this.cacheKelvin = kelvin;
+ 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({
command: 'setColorTemperature',
parameter: `${kelvin}`,
@@ -906,31 +771,29 @@ export class ColorBulb {
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
} else {
this.statusCode(statusCode);
this.statusCode(deviceStatus.statusCode);
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushColorTemperatureChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushColorTemperatureChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No pushColorTemperatureChanges.` +
- `ColorTemperature: ${this.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushColorTemperatureChanges.`
+ + `ColorTemperature: ${this.LightBulb.ColorTemperature}, ColorTemperatureCached: ${this.accessory.context.ColorTemperature}`);
}
}
async pushBrightnessChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushBrightnessChanges`);
- if (this.Brightness !== this.accessory.context.Brightness) {
+ if (this.LightBulb.Brightness !== this.accessory.context.Brightness) {
const bodyChange = JSON.stringify({
command: 'setBrightness',
- parameter: `${this.Brightness}`,
+ parameter: `${this.LightBulb.Brightness}`,
commandType: 'command',
});
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`);
@@ -948,23 +811,20 @@ export class ColorBulb {
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
} else {
this.statusCode(statusCode);
this.statusCode(deviceStatus.statusCode);
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushBrightnessChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushBrightnessChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No pushBrightnessChanges.` +
- `Brightness: ${this.Brightness}, ` +
- `BrightnessCached: ${this.accessory.context.Brightness}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushBrightnessChanges.`
+ + `Brightness: ${this.LightBulb.Brightness}, BrightnessCached: ${this.accessory.context.Brightness}`);
}
}
@@ -972,13 +832,13 @@ export class ColorBulb {
* Handle requests to set the value of the "On" characteristic
*/
async OnSet(value: CharacteristicValue): Promise {
- if (this.On === this.accessory.context.On) {
+ if (this.LightBulb.On === this.accessory.context.On) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set On: ${value}`);
} else {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`);
}
- this.On = value;
+ this.LightBulb.On = value;
this.doColorBulbUpdate.next();
}
@@ -986,15 +846,15 @@ export class ColorBulb {
* Handle requests to set the value of the "Brightness" characteristic
*/
async BrightnessSet(value: CharacteristicValue): Promise {
- if (this.Brightness === this.accessory.context.Brightness) {
+ if (this.LightBulb.Brightness === this.accessory.context.Brightness) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Brightness: ${value}`);
- } else if (this.On) {
+ } else if (this.LightBulb.On) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`);
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`);
}
- this.Brightness = value;
+ this.LightBulb.Brightness = value;
this.doColorBulbUpdate.next();
}
@@ -1002,30 +862,32 @@ export class ColorBulb {
* Handle requests to set the value of the "ColorTemperature" characteristic
*/
async ColorTemperatureSet(value: CharacteristicValue): Promise {
- if (this.ColorTemperature === this.accessory.context.ColorTemperature) {
+ if (this.LightBulb.ColorTemperature === this.accessory.context.ColorTemperature) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set ColorTemperature: ${value}`);
- } else if (this.On) {
+ } else if (this.LightBulb.On) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ColorTemperature: ${value}`);
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set ColorTemperature: ${value}`);
}
+ const minKelvin = 2000;
+ const maxKelvin = 9000;
// Convert mired to kelvin to nearest 100 (SwitchBot seems to need this)
const kelvin = Math.round(1000000 / Number(value) / 100) * 100;
// Check and increase/decrease kelvin to range of device
- const k = Math.min(Math.max(kelvin, this.minKelvin), this.maxKelvin);
+ const k = Math.min(Math.max(kelvin, minKelvin), maxKelvin);
- if (!this.accessory.context.On || this.cacheKelvin === k) {
+ if (!this.accessory.context.On || this.accessory.context.kelvin === k) {
return;
}
// Updating the hue/sat to the corresponding values mimics native adaptive lighting
const hs = m2hs(value);
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, hs[0]);
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, hs[1]);
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Hue, hs[0]);
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Saturation, hs[1]);
- this.ColorTemperature = value;
+ this.LightBulb.ColorTemperature = value;
this.doColorBulbUpdate.next();
}
@@ -1033,17 +895,17 @@ export class ColorBulb {
* Handle requests to set the value of the "Hue" characteristic
*/
async HueSet(value: CharacteristicValue): Promise {
- if (this.Hue === this.accessory.context.Hue) {
+ if (this.LightBulb.Hue === this.accessory.context.Hue) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Hue: ${value}`);
- } else if (this.On) {
+ } else if (this.LightBulb.On) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Hue: ${value}`);
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Hue: ${value}`);
}
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140);
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140);
- this.Hue = value;
+ this.LightBulb.Hue = value;
this.doColorBulbUpdate.next();
}
@@ -1051,60 +913,61 @@ export class ColorBulb {
* Handle requests to set the value of the "Saturation" characteristic
*/
async SaturationSet(value: CharacteristicValue): Promise {
- if (this.Saturation === this.accessory.context.Saturation) {
+ if (this.LightBulb.Saturation === this.accessory.context.Saturation) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Saturation: ${value}`);
- } else if (this.On) {
+ } else if (this.LightBulb.On) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Saturation: ${value}`);
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Saturation: ${value}`);
}
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140);
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, 140);
- this.Saturation = value;
+ this.LightBulb.Saturation = value;
this.doColorBulbUpdate.next();
}
async updateHomeKitCharacteristics(): Promise {
// On
- if (this.On === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.On}`);
+ if (this.LightBulb.On === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.LightBulb.On}`);
} else {
- this.accessory.context.On = this.On;
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.On, this.On);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.On}`);
+ this.accessory.context.On = this.LightBulb.On;
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, this.LightBulb.On);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic On: ${this.LightBulb.On}`);
}
// Brightness
- if (this.Brightness === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.Brightness}`);
+ if (this.LightBulb.Brightness === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Brightness: ${this.LightBulb.Brightness}`);
} else {
- this.accessory.context.Brightness = this.Brightness;
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Brightness, this.Brightness);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.Brightness}`);
+ this.accessory.context.Brightness = this.LightBulb.Brightness;
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Brightness, this.LightBulb.Brightness);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Brightness: ${this.LightBulb.Brightness}`);
}
// ColorTemperature
- if (this.ColorTemperature === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.ColorTemperature}`);
+ if (this.LightBulb.ColorTemperature === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ColorTemperature: ${this.LightBulb.ColorTemperature}`);
} else {
- this.accessory.context.ColorTemperature = this.ColorTemperature;
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, this.ColorTemperature);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic ColorTemperature: ${this.ColorTemperature}`);
+ this.accessory.context.ColorTemperature = this.LightBulb.ColorTemperature;
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, this.LightBulb.ColorTemperature);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` ColorTemperature: ${this.LightBulb.ColorTemperature}`);
}
// Hue
- if (this.Hue === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.Hue}`);
+ if (this.LightBulb.Hue === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Hue: ${this.LightBulb.Hue}`);
} else {
- this.accessory.context.Hue = this.Hue;
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, this.Hue);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Hue: ${this.Hue}`);
+ this.accessory.context.Hue = this.LightBulb.Hue;
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Hue, this.LightBulb.Hue);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Hue: ${this.LightBulb.Hue}`);
}
// Saturation
- if (this.Saturation === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.Saturation}`);
+ if (this.LightBulb.Saturation === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Saturation: ${this.LightBulb.Saturation}`);
} else {
- this.accessory.context.Saturation = this.Saturation;
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, this.Saturation);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Saturation: ${this.Saturation}`);
+ this.accessory.context.Saturation = this.LightBulb.Saturation;
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Saturation, this.LightBulb.Saturation);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Saturation: ${this.LightBulb.Saturation}`);
}
}
@@ -1118,35 +981,6 @@ export class ColorBulb {
}
}
- async stopScanning(switchbot: any) {
- switchbot.stopScan();
- if (this.BLE_IsConnected) {
- await this.BLEparseStatus();
- await this.updateHomeKitCharacteristics();
- } else {
- await this.BLERefreshConnection(switchbot);
- }
- }
-
- async getCustomBLEAddress(switchbot: any) {
- if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} customBLEaddress: ${this.device.customBLEaddress}`);
- (async () => {
- // Start to monitor advertisement packets
- await switchbot.startScan({
- model: 'u',
- });
- // Set an event handler
- switchbot.onadvertisement = (ad: any) => {
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
- };
- await sleep(10000);
- // Stop to monitor
- switchbot.stopScan();
- })();
- }
- }
-
async BLEPushConnection() {
if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`);
@@ -1163,284 +997,17 @@ export class ColorBulb {
}
}
- async retry({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise {
- return fn().catch(async (e: any) => {
- if (max === 0) {
- throw e;
- }
- this.infoLog(e);
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`);
- await sleep(1000);
- return this.retry({ max: max - 1, fn });
- });
- }
-
- maxRetry(): number {
- if (this.device.maxRetry) {
- return this.device.maxRetry;
- } else {
- return 5;
- }
- }
-
- minStep(device: device & devicesConfig): number {
- if (device.colorbulb?.set_minStep) {
- this.set_minStep = device.colorbulb?.set_minStep;
- } else {
- this.set_minStep = 1;
- }
- return this.set_minStep;
- }
-
- async scan(device: device & devicesConfig): Promise {
- if (device.scanDuration) {
- this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
- }
- } else {
- this.scanDuration = this.accessory.context.scanDuration = 1;
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
- }
- }
- }
-
- async statusCode(statusCode: number): Promise {
- switch (statusCode) {
- case 151:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
- break;
- case 152:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
- break;
- case 160:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
- break;
- case 161:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
- this.offlineOff();
- break;
- case 171:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` +
- `Hub: ${this.device.hubDeviceId}`,
- );
- this.offlineOff();
- break;
- case 190:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
- ` Or command format is invalid, statusCode: ${statusCode}`,
- );
- break;
- case 100:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
- break;
- case 200:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
- break;
- case 400:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. `
- + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`);
- break;
- case 401:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, `
- + `but the request has not been authenticated, statusCode: ${statusCode}`);
- break;
- case 403:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not `
- + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`);
- break;
- case 404:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, `
- + `statusCode: ${statusCode}`);
- break;
- case 406:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via `
- + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`);
- break;
- case 415:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType `
- + `header that is not supported by the server, statusCode: ${statusCode}`);
- break;
- case 422:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, The client has made a valid request, `
- + `but the server cannot process it. This is often used for APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
- break;
- case 429:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of `
- + `requests allowed for a given time window, statusCode: ${statusCode}`);
- break;
- case 500:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings `
- + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`);
- break;
- default:
- this.infoLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` +
- `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`,
- );
- }
- }
-
async offlineOff(): Promise {
if (this.device.offline) {
- await this.deviceContext();
- await this.updateHomeKitCharacteristics();
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, false);
}
}
apiError(e: any): void {
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.On, e);
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Hue, e);
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Brightness, e);
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.Saturation, e);
- this.lightBulbService.updateCharacteristic(this.hap.Characteristic.ColorTemperature, e);
- }
-
- async deviceContext(): Promise {
- if (this.On === undefined) {
- this.On = false;
- } else {
- this.On = this.accessory.context.On;
- }
- if (this.Hue === undefined) {
- this.Hue = 0;
- } else {
- this.Hue = this.accessory.context.Hue;
- }
- if (this.Brightness === undefined) {
- this.Brightness = 0;
- } else {
- this.Brightness = this.accessory.context.Brightness;
- }
- if (this.Saturation === undefined) {
- this.Saturation = 0;
- } else {
- this.Saturation = this.accessory.context.Saturation;
- }
- if (this.ColorTemperature === undefined) {
- this.ColorTemperature = 140;
- } else {
- this.ColorTemperature = this.accessory.context.ColorTemperature;
- }
- this.minKelvin = 2000;
- this.maxKelvin = 9000;
- if (this.FirmwareRevision === undefined) {
- this.FirmwareRevision = this.platform.version;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
- }
- }
-
- async refreshRate(device: device & devicesConfig): Promise {
- if (device.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
- } else if (this.platform.config.options!.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
- }
- }
-
- async deviceConfig(device: device & devicesConfig): Promise {
- let config = {};
- if (device.colorbulb) {
- config = device.colorbulb;
- }
- if (device.connectionType !== undefined) {
- config['connectionType'] = device.connectionType;
- }
- if (device.external !== undefined) {
- config['external'] = device.external;
- }
- if (device.logging !== undefined) {
- config['logging'] = device.logging;
- }
- if (device.refreshRate !== undefined) {
- config['refreshRate'] = device.refreshRate;
- }
- if (device.scanDuration !== undefined) {
- config['scanDuration'] = device.scanDuration;
- }
- if (device.offline !== undefined) {
- config['offline'] = device.offline;
- }
- if (device.maxRetry !== undefined) {
- config['maxRetry'] = device.maxRetry;
- }
- if (device.webhook !== undefined) {
- config['webhook'] = device.webhook;
- }
- if (Object.entries(config).length !== 0) {
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
- }
- }
-
- async deviceLogs(device: device & devicesConfig): Promise {
- if (this.platform.debugMode) {
- this.deviceLogging = this.accessory.context.logging = 'debugMode';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
- } else if (device.logging) {
- this.deviceLogging = this.accessory.context.logging = device.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
- } else if (this.platform.config.options?.logging) {
- this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
- } else {
- this.deviceLogging = this.accessory.context.logging = 'standard';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
- }
- }
-
- /**
- * Logging for Device
- */
- infoLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.info(String(...log));
- }
- }
-
- warnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.warn(String(...log));
- }
- }
-
- debugWarnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.warn('[DEBUG]', String(...log));
- }
- }
- }
-
- errorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.error(String(...log));
- }
- }
-
- debugErrorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.error('[DEBUG]', String(...log));
- }
- }
- }
-
- debugLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging === 'debug') {
- this.platform.log.info('[DEBUG]', String(...log));
- } else {
- this.platform.log.debug(String(...log));
- }
- }
- }
-
- enablingDeviceLogging(): boolean {
- return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard';
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.On, e);
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Hue, e);
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Brightness, e);
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.Saturation, e);
+ this.LightBulb.Service.updateCharacteristic(this.hap.Characteristic.ColorTemperature, e);
}
}
diff --git a/src/device/contact.ts b/src/device/contact.ts
index 0c1fba58..43fc399b 100644
--- a/src/device/contact.ts
+++ b/src/device/contact.ts
@@ -1,324 +1,296 @@
-import { request } from 'undici';
-import { sleep } from '../utils.js';
+/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+ *
+ * contact.ts: @switchbot/homebridge-switchbot.
+ */
+import { deviceBase } from './device.js';
import { interval, Subject } from 'rxjs';
+import { Devices } from '../settings.js';
import { skipWhile } from 'rxjs/operators';
-import { SwitchBotPlatform } from '../platform.js';
-import { Service, PlatformAccessory, CharacteristicValue, API, Logging, HAP } from 'homebridge';
-import { device, devicesConfig, serviceData, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js';
+
+import type { SwitchBotPlatform } from '../platform.js';
+import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
+import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js';
/**
* Platform Accessory
* An instance of this class is created for each accessory your platform registers
* Each accessory may expose multiple services of different service types.
*/
-export class Contact {
- public readonly api: API;
- public readonly log: Logging;
- public readonly config!: SwitchBotPlatformConfig;
- protected readonly hap: HAP;
+export class Contact extends deviceBase {
// Services
- motionService?: Service;
- batteryService: Service;
- lightSensorService?: Service;
- contactSensorservice: Service;
-
- // Characteristic Values
- BatteryLevel!: CharacteristicValue;
- MotionDetected!: CharacteristicValue;
- StatusLowBattery!: CharacteristicValue;
- FirmwareRevision!: CharacteristicValue;
- ContactSensorState!: CharacteristicValue;
- CurrentAmbientLightLevel!: CharacteristicValue;
-
- // OpenAPI Status
- OpenAPI_BatteryLevel: deviceStatus['battery'];
- OpenAPI_FirmwareRevision: deviceStatus['version'];
- OpenAPI_MotionDetected: deviceStatus['moveDetected'];
- OpenAPI_ContactSensorState: deviceStatus['openState'];
- OpenAPI_CurrentAmbientLightLevel: deviceStatus['brightness'];
-
- // BLE Status
- BLE_BatteryLevel!: serviceData['battery'];
- BLE_MotionDetected!: serviceData['movement'];
- BLE_ContactSensorState!: serviceData['doorState'];
- BLE_CurrentAmbientLightLevel: serviceData['lightLevel'];
-
- // BLE Others
- scanning!: boolean;
- BLE_IsConnected?: boolean;
-
- // Config
- set_minLux!: number;
- set_maxLux!: number;
- scanDuration!: number;
- deviceLogging!: string;
- deviceRefreshRate!: number;
+ private ContactSensor: {
+ Name: CharacteristicValue;
+ Service: Service;
+ ContactSensorState: CharacteristicValue;
+ };
+
+ private Battery: {
+ Name: CharacteristicValue;
+ Service: Service;
+ BatteryLevel: CharacteristicValue;
+ StatusLowBattery: CharacteristicValue;
+ };
+
+ private MotionSensor?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ MotionDetected: CharacteristicValue;
+ };
+
+ private LightSensor?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ CurrentAmbientLightLevel: CharacteristicValue;
+ };
// Updates
- contactUbpdateInProgress!: boolean;
+ contactUpdateInProgress!: boolean;
doContactUpdate!: Subject;
- // Connection
- private readonly OpenAPI: boolean;
- private readonly BLE: boolean;
-
constructor(
- private readonly platform: SwitchBotPlatform,
- private accessory: PlatformAccessory,
- public device: device & devicesConfig,
+ readonly platform: SwitchBotPlatform,
+ accessory: PlatformAccessory,
+ device: device & devicesConfig,
) {
- this.api = this.platform.api;
- this.log = this.platform.log;
- this.config = this.platform.config;
- this.hap = this.api.hap;
- // Connection
- this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI';
- this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI';
- // default placeholders
- this.deviceLogs(device);
- this.scan(device);
- this.refreshRate(device);
- this.deviceContext();
- this.deviceConfig(device);
-
+ super(platform, accessory, device);
// this is subject we use to track when we need to POST changes to the SwitchBot API
this.doContactUpdate = new Subject();
- this.contactUbpdateInProgress = false;
-
- // Retrieve initial values and updateHomekit
- this.refreshStatus();
-
- // set accessory information
- accessory
- .getService(this.hap.Service.AccessoryInformation)!
- .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
- .setCharacteristic(this.hap.Characteristic.Model, 'W1201500')
- .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId)
- .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
-
- // get the Contact service if it exists, otherwise create a new Contact service
- // you can create multiple services for each accessory
- const contactSensorservice = `${accessory.displayName} Contact Sensor`;
- (this.contactSensorservice = accessory.getService(this.hap.Service.ContactSensor)
- || accessory.addService(this.hap.Service.ContactSensor)), contactSensorservice;
+ this.contactUpdateInProgress = false;
+
+ // Initialize Contact Sensor Service
+ accessory.context.ContactSensor = accessory.context.ContactSensor ?? {};
+ this.ContactSensor = {
+ Name: accessory.context.ContactSensor.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.ContactSensor) ?? accessory.addService(this.hap.Service.ContactSensor) as Service,
+ ContactSensorState: accessory.context.ContactSensorState ?? this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED,
+ };
+ accessory.context.ContactSensor = this.ContactSensor as object;
+
+ // Initialize ContactSensor Characteristics
+ this.ContactSensor.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.ContactSensor.Name)
+ .setCharacteristic(this.hap.Characteristic.StatusActive, true)
+ .getCharacteristic(this.hap.Characteristic.ContactSensorState)
+ .onGet(() => {
+ return this.ContactSensor.ContactSensorState;
+ });
- this.contactSensorservice.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.contactSensorservice.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.contactSensorservice.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
- }
+ // Initialize Battery Service
+ accessory.context.Battery = accessory.context.Battery ?? {};
+ this.Battery = {
+ Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`,
+ Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service,
+ BatteryLevel: accessory.context.BatteryLevel ?? 100,
+ StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL,
+ };
+ accessory.context.Battery = this.Battery as object;
+
+ // Initialize Battery Characteristics
+ this.Battery.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name)
+ .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE)
+ .getCharacteristic(this.hap.Characteristic.BatteryLevel)
+ .onGet(() => {
+ return this.Battery.BatteryLevel;
+ });
- // Motion Sensor Service
- if (device.contact?.hide_motionsensor) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Motion Sensor Service`);
- this.motionService = this.accessory.getService(this.hap.Service.MotionSensor);
- accessory.removeService(this.motionService!);
- } else if (!this.motionService) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Motion Sensor Service`);
- const motionService = `${accessory.displayName} Motion Sensor`;
- (this.motionService = this.accessory.getService(this.hap.Service.MotionSensor)
- || this.accessory.addService(this.hap.Service.MotionSensor)), motionService;
+ this.Battery.Service
+ .setCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery)
+ .getCharacteristic(this.hap.Characteristic.StatusLowBattery)
+ .onGet(() => {
+ return this.Battery.StatusLowBattery;
+ });
- this.motionService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Motion Sensor`);
- this.motionService.setCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Motion Sensor`);
+ // Initialize Motion Sensor Service
+ if (this.device.contact?.hide_motionsensor) {
+ if (this.MotionSensor) {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} Removing Motion Sensor Service`);
+ this.MotionSensor.Service = accessory.getService(this.hap.Service.MotionSensor) as Service;
+ accessory.removeService(this.MotionSensor.Service);
+ }
} else {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Motion Sensor Service Not Added`);
+ accessory.context.MotionSensor = accessory.context.MotionSensor ?? {};
+ this.MotionSensor = {
+ Name: accessory.context.MotionSensor.Name ?? `${accessory.displayName} Motion Sensor`,
+ Service: accessory.getService(this.hap.Service.MotionSensor) ?? accessory.addService(this.hap.Service.MotionSensor) as Service,
+ MotionDetected: accessory.context.MotionDetected ?? false,
+ };
+ accessory.context.MotionSensor = this.MotionSensor as object;
+
+ // Motion Sensor Characteristics
+ this.MotionSensor.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.MotionSensor.Name)
+ .setCharacteristic(this.hap.Characteristic.StatusActive, true)
+ .getCharacteristic(this.hap.Characteristic.MotionDetected)
+ .onGet(() => {
+ return this.MotionSensor!.MotionDetected;
+ });
}
- // Light Sensor Service
+ // Initialize Light Sensor Service
if (device.contact?.hide_lightsensor) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`);
- this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor);
- accessory.removeService(this.lightSensorService!);
- } else if (!this.lightSensorService) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`);
-
- const lightSensorService = `${accessory.displayName} Light Sensor`;
- (this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor)
- || this.accessory.addService(this.hap.Service.LightSensor)), lightSensorService;
-
- this.lightSensorService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Light Sensor`);
- this.lightSensorService.setCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Light Sensor`);
+ if (this.LightSensor) {
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`);
+ this.LightSensor.Service = accessory.getService(this.hap.Service.LightSensor) as Service;
+ accessory.removeService(this.LightSensor.Service);
+ }
} else {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Light Sensor Service Not Added`);
+ accessory.context.LightSensor = accessory.context.LightSensor ?? {};
+ this.LightSensor = {
+ Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`,
+ Service: accessory.getService(this.hap.Service.LightSensor) ?? accessory.addService(this.hap.Service.LightSensor) as Service,
+ CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001,
+ };
+ accessory.context.LightSensor = this.LightSensor as object;
+
+ // Light Sensor Characteristics
+ this.LightSensor.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.LightSensor.Name)
+ .setCharacteristic(this.hap.Characteristic.StatusActive, true)
+ .getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel)
+ .onGet(() => {
+ return this.LightSensor!.CurrentAmbientLightLevel;
+ });
}
- // Battery Service
- const batteryService = `${accessory.displayName} Battery`;
- (this.batteryService = this.accessory.getService(this.hap.Service.Battery)
- || accessory.addService(this.hap.Service.Battery)), batteryService;
-
- this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`);
- if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`);
- }
- this.batteryService.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE);
+ // Retrieve initial values and updateHomekit
+ this.refreshStatus();
// Retrieve initial values and updateHomekit
this.updateHomeKitCharacteristics();
// Start an update interval
interval(this.deviceRefreshRate * 1000)
- .pipe(skipWhile(() => this.contactUbpdateInProgress))
+ .pipe(skipWhile(() => this.contactUpdateInProgress))
.subscribe(async () => {
await this.refreshStatus();
});
//regisiter webhook event handler
- if (this.device.webhook) {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`);
- this.platform.webhookEventHandler[this.device.deviceId] = async (context) => {
- try {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
- const { detectionState, brightness, openState } = context;
- const { MotionDetected, CurrentAmbientLightLevel, ContactSensorState } = this;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` +
- '(detectionState, brightness, openState) = ' +
- `Webhook:(${detectionState}, ${brightness}, ${openState}), ` +
- `current:(${MotionDetected}, ${CurrentAmbientLightLevel}, ${ContactSensorState})`);
- this.set_minLux = this.minLux();
- this.set_maxLux = this.maxLux();
- this.MotionDetected = detectionState === 'DETECTED' ? true : false;
- this.CurrentAmbientLightLevel = brightness === 'bright' ? this.set_maxLux : this.set_minLux;
- this.ContactSensorState = openState === 'open' ? 1 : 0;
- this.updateHomeKitCharacteristics();
- } catch (e: any) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
- + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
- }
- };
- }
+ this.registerWebhook(accessory, device);
}
- /**
- * Parse the device status from the SwitchBot api
- */
- async parseStatus(): Promise {
- if (!this.device.enableCloudService && this.OpenAPI) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`);
- } else if (this.BLE) {
- await this.BLEparseStatus();
- } else if (this.OpenAPI && this.platform.config.credentials?.token) {
- await this.openAPIparseStatus();
- } else {
- await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`,
- );
- }
- }
-
- async BLEparseStatus(): Promise {
+ async BLEparseStatus(serviceData: serviceData): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
// Door State
- switch (this.BLE_ContactSensorState) {
+ switch (serviceData.doorState) {
case 'open':
case 1:
- this.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED;
+ this.ContactSensor.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED;
break;
case 'close':
case 0:
- this.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED;
+ this.ContactSensor.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED;
break;
default:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} timeout no closed, doorstate: ${this.BLE_ContactSensorState}`);
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} timeout no closed, doorstate: ${serviceData.doorState}`);
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensor.ContactSensorState}`);
if (
- this.ContactSensorState !== this.accessory.context.ContactSensorState &&
+ this.ContactSensor.ContactSensorState !== this.accessory.context.ContactSensorState &&
this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED
) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Opened`);
}
// Movement
if (!this.device.contact?.hide_motionsensor) {
- this.MotionDetected = Boolean(this.BLE_MotionDetected);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
- if (this.MotionDetected !== this.accessory.context.MotionDetected && this.MotionDetected) {
+ this.MotionSensor!.MotionDetected = Boolean(serviceData.movement);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionSensor!.MotionDetected}`);
+ if (this.MotionSensor!.MotionDetected !== this.accessory.context.MotionDetected && this.MotionSensor!.MotionDetected) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Detected Motion`);
}
}
// Light Level
if (!this.device.contact?.hide_lightsensor) {
- this.set_minLux = this.minLux();
- this.set_maxLux = this.maxLux();
- switch (this.BLE_CurrentAmbientLightLevel) {
+ const set_minLux = this.device.contact?.set_minLux ?? 1;
+ const set_maxLux = this.device.contact?.set_maxLux ?? 6001;
+ switch (serviceData.lightLevel) {
case true:
- this.CurrentAmbientLightLevel = this.set_minLux;
+ this.LightSensor!.CurrentAmbientLightLevel = set_minLux;
break;
default:
- this.CurrentAmbientLightLevel = this.set_maxLux;
+ this.LightSensor!.CurrentAmbientLightLevel = set_maxLux;
}
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` +
- ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
- );
- if (this.CurrentAmbientLightLevel !== this.accessory.context.CurrentAmbientLightLevel) {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},`
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
+ if (this.LightSensor!.CurrentAmbientLightLevel !== this.accessory.context.CurrentAmbientLightLevel) {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
}
}
// Battery
- if (this.BLE_BatteryLevel === undefined) {
- this.BLE_BatteryLevel === 100;
+ if (serviceData.battery === undefined) {
+ serviceData.battery === 100;
}
- this.BatteryLevel = Number(this.BLE_BatteryLevel);
- if (this.BatteryLevel < 10) {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
+ this.Battery.BatteryLevel = Number(serviceData.battery);
+ if (this.Battery.BatteryLevel < 10) {
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
} else {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
}
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, ` + `StatusLowBattery: ${this.StatusLowBattery}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, `
+ + `StatusLowBattery: ${this.Battery.StatusLowBattery}`);
}
- async openAPIparseStatus(): Promise {
+ async openAPIparseStatus(deviceStatus: deviceStatus): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
// Contact State
- if (this.OpenAPI_ContactSensorState === 'open') {
- this.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`);
- } else if (this.OpenAPI_ContactSensorState === 'close') {
- this.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`);
+ if (deviceStatus.body.openState === 'open') {
+ this.ContactSensor.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_NOT_DETECTED;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensor.ContactSensorState}`);
+ } else if (deviceStatus.body.openState === 'close') {
+ this.ContactSensor.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensor.ContactSensorState}`);
} else {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openState: ${this.OpenAPI_ContactSensorState}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openState: ${deviceStatus.body.openState}`);
}
// Motion State
if (!this.device.contact?.hide_motionsensor) {
- this.MotionDetected = this.OpenAPI_MotionDetected!;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
+ this.MotionSensor!.MotionDetected = deviceStatus.body.moveDetected!;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionSensor!.MotionDetected}`);
}
// Light Level
if (!this.device.contact?.hide_lightsensor) {
- this.set_minLux = this.minLux();
- this.set_maxLux = this.maxLux();
- switch (this.OpenAPI_CurrentAmbientLightLevel) {
+ const set_minLux = this.device.contact?.set_minLux ?? 1;
+ const set_maxLux = this.device.contact?.set_maxLux ?? 6001;
+ switch (deviceStatus.body.brightness) {
case 'dim':
- this.CurrentAmbientLightLevel = this.set_minLux;
+ this.LightSensor!.CurrentAmbientLightLevel = set_minLux;
break;
case 'bright':
default:
- this.CurrentAmbientLightLevel = this.set_maxLux;
+ this.LightSensor!.CurrentAmbientLightLevel = set_maxLux;
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
}
// Battery
- if (this.OpenAPI_BatteryLevel === undefined) {
- this.OpenAPI_BatteryLevel === 100;
+ if (deviceStatus.body.battery === undefined) {
+ deviceStatus.body.battery === 100;
}
- this.BatteryLevel = Number(this.OpenAPI_BatteryLevel);
- if (this.BatteryLevel < 10) {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
+ this.Battery.BatteryLevel = Number(deviceStatus.body.battery);
+ if (this.Battery.BatteryLevel < 10) {
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
} else {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
+ }
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, `
+ + `StatusLowBattery: ${this.Battery.StatusLowBattery}`);
+
+ // Firmware Version
+ const version = deviceStatus.body.version?.toString();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
+ if (deviceStatus.body.version) {
+ const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
+ this.accessory
+ .getService(this.hap.Service.AccessoryInformation)!
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
+ .updateValue(deviceVersion);
+ this.accessory.context.deviceVersion = deviceVersion;
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
}
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, ` + `StatusLowBattery: ${this.StatusLowBattery}`,
- );
-
- // FirmwareRevision
- this.FirmwareRevision = this.OpenAPI_FirmwareRevision!;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
}
/**
@@ -333,10 +305,8 @@ export class Contact {
await this.openAPIRefreshStatus();
} else {
await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` +
- ` ${this.device.connectionType}, refreshStatus will not happen.`,
- );
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
}
}
@@ -344,126 +314,95 @@ export class Contact {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLERefreshStatus`);
const switchbot = await this.platform.connectBLE();
// Convert to BLE Address
- this.device.bleMac =
- this.device.customBLEaddress ||
- this.device
- .deviceId!.match(/.{1,2}/g)!
- .join(':')
- .toLowerCase();
+ this.device.bleMac = this.device
+ .deviceId!.match(/.{1,2}/g)!
+ .join(':')
+ .toLowerCase();
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
this.getCustomBLEAddress(switchbot);
// Start to monitor advertisement packets
(async () => {
- await switchbot.startScan({
- model: 'd',
- id: this.device.bleMac,
- });
+ // Start to monitor advertisement packets
+ await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac });
// Set an event handler
switchbot.onadvertisement = (ad: any) => {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`);
- if (this.device.bleMac === ad.address && ad.serviceData.model === 'd') {
+ if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.BLE_MotionDetected = ad.serviceData.movement;
- this.BLE_BatteryLevel = ad.serviceData.battery;
- this.BLE_CurrentAmbientLightLevel = ad.serviceData.lightLevel;
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
}
};
- // Wait 1 seconds
+ // Wait 10 seconds
await switchbot.wait(this.scanDuration * 1000);
// Stop to monitor
await switchbot.stopScan();
// Update HomeKit
- await this.BLEparseStatus();
+ await this.BLEparseStatus(switchbot.onadvertisement.serviceData);
await this.updateHomeKitCharacteristics();
})();
- /*if (switchbot !== false) {
- await switchbot
- .startScan({
- model: 'd',
- id: this.device.bleMac,
- })
- .then(async () => {
- // Set an event handler
- this.scanning = true;
- switchbot.onadvertisement = async (ad: ad) => {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` +
- ` BLE Address Found: ${ad.address}`,
- );
- this.BLE_MotionDetected = ad.serviceData.movement;
- this.BLE_BatteryLevel = ad.serviceData.battery;
- this.BLE_ContactSensorState = ad.serviceData.doorState;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} movement: ${ad.serviceData.movement}, doorState: ` +
- `${ad.serviceData.doorState}, battery: ${ad.serviceData.battery}`,
- );
-
- if (ad.serviceData) {
- this.BLE_IsConnected = true;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
- await this.stopScanning(switchbot);
- this.scanning = false;
- } else {
- this.BLE_IsConnected = false;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
- }
- };
- // Wait
- return await sleep(this.scanDuration * 1000);
- })
- .then(async () => {
- // Stop to monitor
- if (this.scanning) {
- await this.stopScanning(switchbot);
- }
- })
- .catch(async (e: any) => {
- this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
- await this.BLERefreshConnection(switchbot);
- });
- } else {
+ if (switchbot === undefined) {
await this.BLERefreshConnection(switchbot);
- }*/
+ }
}
async openAPIRefreshStatus(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
try {
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, {
- headers: this.platform.generateHeaders(),
- });
+ const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries,
+ `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() });
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
const deviceStatus: any = await body.json();
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
- this.OpenAPI_ContactSensorState = deviceStatus.body.openState;
- this.OpenAPI_MotionDetected = deviceStatus.body.moveDetected;
- this.OpenAPI_CurrentAmbientLightLevel = deviceStatus.body.brightness;
- this.OpenAPI_BatteryLevel = deviceStatus.body.battery;
- this.OpenAPI_FirmwareRevision = deviceStatus.body.version;
- this.openAPIparseStatus();
+ this.openAPIparseStatus(deviceStatus);
this.updateHomeKitCharacteristics();
} else {
this.statusCode(statusCode);
this.statusCode(deviceStatus.statusCode);
+ if (deviceStatus.statusCode === 161 || deviceStatus.statusCode === 171) {
+ this.offlineOff();
+ }
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
+ }
+ }
+
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
+ if (device.webhook) {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`);
+ this.platform.webhookEventHandler[device.deviceId] = async (context) => {
+ try {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
+ const { detectionState, brightness, openState } = context;
+ const { ContactSensorState } = this.ContactSensor;
+ const { CurrentAmbientLightLevel } = this.LightSensor ?? {};
+ const { MotionDetected } = this.MotionSensor ?? {};
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} (detectionState, brightness, openState) = Webhook:(${detectionState}, `
+ + `${brightness}, ${openState}), current:(${MotionDetected}, ${CurrentAmbientLightLevel}, ${ContactSensorState})`);
+ const set_minLux = this.device.contact?.set_minLux ?? 1;
+ const set_maxLux = this.device.contact?.set_maxLux ?? 6001;
+ this.ContactSensor.ContactSensorState = openState === 'open' ? 1 : 0;
+ if (!device.contact?.hide_motionsensor) {
+ this.MotionSensor!.MotionDetected = detectionState === 'DETECTED' ? true : false;
+ }
+ if (!device.contact?.hide_lightsensor) {
+ this.LightSensor!.CurrentAmbientLightLevel = brightness === 'bright' ? set_maxLux : set_minLux;
+ }
+ this.updateHomeKitCharacteristics();
+ } catch (e: any) {
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
+ }
+ };
+ } else {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`);
}
}
@@ -471,75 +410,49 @@ export class Contact {
* Updates the status for each of the HomeKit Characteristics
*/
async updateHomeKitCharacteristics(): Promise {
- if (this.ContactSensorState === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensorState}`);
+ if (this.ContactSensor.ContactSensorState === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ContactSensorState: ${this.ContactSensor.ContactSensorState}`);
} else {
- this.accessory.context.ContactSensorState = this.ContactSensorState;
- this.contactSensorservice.updateCharacteristic(this.hap.Characteristic.ContactSensorState, this.ContactSensorState);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic ContactSensorState: ${this.ContactSensorState}`);
+ this.accessory.context.ContactSensorState = this.ContactSensor.ContactSensorState;
+ this.ContactSensor.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState, this.ContactSensor.ContactSensorState);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` ContactSensorState: ${this.ContactSensor.ContactSensorState}`);
}
if (!this.device.contact?.hide_motionsensor) {
- if (this.MotionDetected === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionDetected}`);
+ if (this.MotionSensor!.MotionDetected === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MotionDetected: ${this.MotionSensor!.MotionDetected}`);
} else {
- this.accessory.context.MotionDetected = this.MotionDetected;
- this.motionService?.updateCharacteristic(this.hap.Characteristic.MotionDetected, this.MotionDetected);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic MotionDetected: ${this.MotionDetected}`);
+ this.accessory.context.MotionDetected = this.MotionSensor!.MotionDetected;
+ this.MotionSensor!.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, this.MotionSensor!.MotionDetected);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` MotionDetected: ${this.MotionSensor!.MotionDetected}`);
}
}
if (!this.device.contact?.hide_lightsensor) {
- if (this.CurrentAmbientLightLevel === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
+ if (this.LightSensor?.CurrentAmbientLightLevel === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentAmbientLightLevel: ${this.LightSensor?.CurrentAmbientLightLevel}`);
} else {
- this.accessory.context.CurrentAmbientLightLevel = this.CurrentAmbientLightLevel;
- this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` +
- ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
- );
+ this.accessory.context.CurrentAmbientLightLevel = this.LightSensor.CurrentAmbientLightLevel;
+ this.LightSensor.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor.CurrentAmbientLightLevel);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor.CurrentAmbientLightLevel}`);
}
}
- if (this.BatteryLevel === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
- } else {
- this.accessory.context.BatteryLevel = this.BatteryLevel;
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
- }
- if (this.StatusLowBattery === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
+ if (this.Battery.BatteryLevel === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`);
} else {
- this.accessory.context.StatusLowBattery = this.StatusLowBattery;
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
+ this.accessory.context.BatteryLevel = this.Battery.BatteryLevel;
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`);
}
- }
-
- async stopScanning(switchbot: any) {
- switchbot.stopScan();
- if (this.BLE_IsConnected) {
- await this.BLEparseStatus();
- await this.updateHomeKitCharacteristics();
+ if (this.Battery.StatusLowBattery === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`);
} else {
- await this.BLERefreshConnection(switchbot);
- }
- }
-
- async getCustomBLEAddress(switchbot: any) {
- if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
- (async () => {
- // Start to monitor advertisement packets
- await switchbot.startScan({
- model: 'd',
- });
- // Set an event handler
- switchbot.onadvertisement = (ad: any) => {
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
- };
- await sleep(10000);
- // Stop to monitor
- switchbot.stopScan();
- })();
+ this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery;
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
}
}
@@ -552,254 +465,31 @@ export class Contact {
}
}
- minLux(): number {
- if (this.device.contact?.set_minLux) {
- this.set_minLux = this.device.contact!.set_minLux!;
- } else {
- this.set_minLux = 1;
- }
- return this.set_minLux;
- }
-
- maxLux(): number {
- if (this.device.contact?.set_maxLux) {
- this.set_maxLux = this.device.contact!.set_maxLux!;
- } else {
- this.set_maxLux = 6001;
- }
- return this.set_maxLux;
- }
-
- async scan(device: device & devicesConfig): Promise {
- if (device.scanDuration) {
- this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
- }
- } else {
- this.scanDuration = this.accessory.context.scanDuration = 1;
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
- }
- }
- }
-
- async statusCode(statusCode: number): Promise {
- switch (statusCode) {
- case 151:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
- break;
- case 152:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
- break;
- case 160:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
- break;
- case 161:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
- this.offlineOff();
- break;
- case 171:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` +
- `Hub: ${this.device.hubDeviceId}`,
- );
- this.offlineOff();
- break;
- case 190:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
- ` Or command format is invalid, statusCode: ${statusCode}`,
- );
- break;
- case 100:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
- break;
- case 200:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
- break;
- case 400:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. `
- + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`);
- break;
- case 401:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, `
- + `but the request has not been authenticated, statusCode: ${statusCode}`);
- break;
- case 403:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not `
- + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`);
- break;
- case 404:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, `
- + `statusCode: ${statusCode}`);
- break;
- case 406:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via `
- + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`);
- break;
- case 415:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType `
- + `header that is not supported by the server, statusCode: ${statusCode}`);
- break;
- case 422:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, The client has made a valid request, `
- + `but the server cannot process it. This is often used for APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
- break;
- case 429:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of `
- + `requests allowed for a given time window, statusCode: ${statusCode}`);
- break;
- case 500:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings `
- + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`);
- break;
- default:
- this.infoLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` +
- `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`,
- );
- }
- }
-
async offlineOff(): Promise {
if (this.device.offline) {
- await this.deviceContext();
- await this.updateHomeKitCharacteristics();
+ this.ContactSensor.Service.updateCharacteristic(this.hap.Characteristic.ContactSensorState,
+ this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED);
+ if (!this.device.contact?.hide_motionsensor) {
+ this.MotionSensor!.Service.updateCharacteristic(this.hap.Characteristic.MotionDetected, false);
+ }
+ if (!this.device.contact?.hide_lightsensor) {
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, 100);
+ }
}
}
async apiError(e: any): Promise {
- this.contactSensorservice.updateCharacteristic(this.hap.Characteristic.ContactSensorState, e);
+ 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.motionService?.updateCharacteristic(this.hap.Characteristic.MotionDetected, e);
+ 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.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e);
- }
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e);
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e);
- }
-
- async deviceContext() {
- if (this.MotionDetected === undefined) {
- this.MotionDetected = false;
- } else {
- this.MotionDetected = this.accessory.context.MotionDetected;
- }
- if (this.ContactSensorState === undefined) {
- this.ContactSensorState = this.hap.Characteristic.ContactSensorState.CONTACT_DETECTED;
- } else {
- this.ContactSensorState = this.accessory.context.ContactSensorState;
- }
- if (this.FirmwareRevision === undefined) {
- this.FirmwareRevision = this.platform.version;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
- }
- }
-
- async refreshRate(device: device & devicesConfig): Promise {
- if (device.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
- } else if (this.platform.config.options!.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
- }
- }
-
- async deviceConfig(device: device & devicesConfig): Promise {
- let config = {};
- if (device.contact) {
- config = device.contact;
- }
- if (device.connectionType !== undefined) {
- config['connectionType'] = device.connectionType;
- }
- if (device.external !== undefined) {
- config['external'] = device.external;
- }
- if (device.logging !== undefined) {
- config['logging'] = device.logging;
- }
- if (device.refreshRate !== undefined) {
- config['refreshRate'] = device.refreshRate;
- }
- if (device.scanDuration !== undefined) {
- config['scanDuration'] = device.scanDuration;
- }
- if (device.webhook !== undefined) {
- config['webhook'] = device.webhook;
- }
- if (Object.entries(config).length !== 0) {
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e);
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e);
}
- }
-
- async deviceLogs(device: device & devicesConfig): Promise {
- if (this.platform.debugMode) {
- this.deviceLogging = this.accessory.context.logging = 'debugMode';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
- } else if (device.logging) {
- this.deviceLogging = this.accessory.context.logging = device.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
- } else if (this.platform.config.options?.logging) {
- this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
- } else {
- this.deviceLogging = this.accessory.context.logging = 'standard';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
- }
- }
-
- /**
- * Logging for Device
- */
- infoLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.info(String(...log));
- }
- }
-
- warnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.warn(String(...log));
- }
- }
-
- debugWarnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.warn('[DEBUG]', String(...log));
- }
- }
- }
-
- errorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.error(String(...log));
- }
- }
-
- debugErrorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.error('[DEBUG]', String(...log));
- }
- }
- }
-
- debugLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging === 'debug') {
- this.platform.log.info('[DEBUG]', String(...log));
- } else {
- this.platform.log.debug(String(...log));
- }
- }
- }
-
- enablingDeviceLogging(): boolean {
- return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard';
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e);
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e);
}
}
diff --git a/src/device/curtain.ts b/src/device/curtain.ts
index 94c04fe6..56ae75c9 100644
--- a/src/device/curtain.ts
+++ b/src/device/curtain.ts
@@ -1,193 +1,181 @@
+/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+ *
+ * curtain.ts: @switchbot/homebridge-switchbot.
+ */
+import { hostname } from 'os';
import { request } from 'undici';
-import { sleep } from '../utils.js';
-import { MqttClient } from 'mqtt';
import { interval, Subject } from 'rxjs';
-import asyncmqtt from 'async-mqtt';
-import { SwitchBotPlatform } from '../platform.js';
+import { deviceBase } from './device.js';
+import { Devices } from '../settings.js';
import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
-import { Service, PlatformAccessory, CharacteristicValue, CharacteristicChange, API, Logging, HAP } from 'homebridge';
-import { device, devicesConfig, serviceData, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js';
-import { hostname } from 'os';
-
-export class Curtain {
- public readonly api: API;
- public readonly log: Logging;
- public readonly config!: SwitchBotPlatformConfig;
- protected readonly hap: HAP;
- // Services
- batteryService: Service;
- lightSensorService?: Service;
- windowCoveringService!: Service;
- // Characteristic Values
- BatteryLevel!: CharacteristicValue;
- HoldPosition!: CharacteristicValue;
- PositionState!: CharacteristicValue;
- TargetPosition!: CharacteristicValue;
- CurrentPosition!: CharacteristicValue;
- FirmwareRevision!: CharacteristicValue;
- StatusLowBattery!: CharacteristicValue;
- CurrentAmbientLightLevel?: CharacteristicValue;
+import type { SwitchBotPlatform } from '../platform.js';
+import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js';
+import type { Service, PlatformAccessory, CharacteristicValue, CharacteristicChange } from 'homebridge';
- // OpenAPI Status
- OpenAPI_InMotion: deviceStatus['moving'];
- OpenAPI_BatteryLevel: deviceStatus['battery'];
- OpenAPI_FirmwareRevision: deviceStatus['version'];
- OpenAPI_CurrentPosition: deviceStatus['slidePosition'];
- OpenAPI_CurrentAmbientLightLevel: deviceStatus['brightness'];
-
- // Others
- Mode!: string;
- setPositionMode?: string;
-
- // BLE Status
- BLE_InMotion: serviceData['inMotion'];
- BLE_BatteryLevel: serviceData['battery'];
- BLE_Calibration: serviceData['calibration'];
- BLE_CurrentPosition: serviceData['position'];
- BLE_CurrentAmbientLightLevel: serviceData['lightLevel'];
-
- // BLE Others
- BLE_IsConnected?: boolean;
- spaceBetweenLevels!: number;
+export class Curtain extends deviceBase {
+ // Services
+ private WindowCovering: {
+ Name: CharacteristicValue;
+ Service: Service;
+ PositionState: CharacteristicValue;
+ TargetPosition: CharacteristicValue;
+ CurrentPosition: CharacteristicValue;
+ HoldPosition: CharacteristicValue;
+ };
+
+ private Battery: {
+ Name: CharacteristicValue;
+ Service: Service;
+ BatteryLevel: CharacteristicValue;
+ StatusLowBattery: CharacteristicValue;
+ ChargingState: CharacteristicValue;
+ };
+
+ private LightSensor?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ CurrentAmbientLightLevel?: CharacteristicValue;
+ };
// Target
setNewTarget!: boolean;
setNewTargetTimer!: NodeJS.Timeout;
- //MQTT stuff
- mqttClient: MqttClient | null = null;
-
- // Config
- updateRate!: number;
- set_minLux!: number;
- set_maxLux!: number;
- set_minStep!: number;
- setOpenMode!: string;
- setCloseMode!: string;
- scanDuration!: number;
- deviceLogging!: string;
- deviceRefreshRate!: number;
-
// Updates
curtainUpdateInProgress!: boolean;
doCurtainUpdate!: Subject;
- // Connection
- private readonly OpenAPI: boolean;
- private readonly BLE: boolean;
-
- // EVE history service handler
- historyService: any = null;
-
constructor(
- private readonly platform: SwitchBotPlatform,
- private accessory: PlatformAccessory,
- public device: device & devicesConfig,
+ readonly platform: SwitchBotPlatform,
+ accessory: PlatformAccessory,
+ device: device & devicesConfig,
) {
- this.api = this.platform.api;
- this.log = this.platform.log;
- this.config = this.platform.config;
- this.hap = this.api.hap;
- // Connection
- this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI';
- this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI';
- // default placeholders
- this.deviceLogs(device);
- this.refreshRate(device);
- this.scan(device);
- this.setupMqtt(device);
- this.deviceContext();
- this.deviceConfig(device);
-
+ super(platform, accessory, device);
+ // default placeholder
+ this.history(device);
// this is subject we use to track when we need to POST changes to the SwitchBot API
this.doCurtainUpdate = new Subject();
this.curtainUpdateInProgress = false;
this.setNewTarget = false;
- // Retrieve initial values and updateHomekit
- this.refreshStatus();
-
- // set accessory information
- accessory
- .getService(this.hap.Service.AccessoryInformation)!
- .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
- .setCharacteristic(this.hap.Characteristic.Model, 'W0701600')
- .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId)
- .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
-
- // get the WindowCovering service if it exists, otherwise create a new WindowCovering service
- // you can create multiple services for each accessory
- const windowCoveringService = `${accessory.displayName} ${device.deviceType}`;
- (this.windowCoveringService = accessory.getService(this.hap.Service.WindowCovering)
- || accessory.addService(this.hap.Service.WindowCovering)), windowCoveringService;
-
- this.windowCoveringService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.windowCoveringService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.windowCoveringService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
- }
-
- // each service must implement at-minimum the "required characteristics" for the given service type
- // see https://developers.homebridge.io/#/service/WindowCovering
-
- // create handlers for required characteristics
- this.windowCoveringService.setCharacteristic(this.hap.Characteristic.PositionState, this.PositionState);
+ // Initialize WindowCovering Service
+ accessory.context.WindowCovering = accessory.context.WindowCovering ?? {};
+ this.WindowCovering = {
+ Name: accessory.context.WindowCovering.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.WindowCovering) ?? accessory.addService(this.hap.Service.WindowCovering) as Service,
+ PositionState: accessory.context.PositionState ?? this.hap.Characteristic.PositionState.STOPPED,
+ TargetPosition: accessory.context.TargetPosition ?? 100,
+ CurrentPosition: accessory.context.CurrentPosition ?? 100,
+ HoldPosition: accessory.context.HoldPosition ?? false,
+ };
+ accessory.context.WindowCovering = this.WindowCovering as object;
+
+ // Initialize WindowCovering Service
+ this.WindowCovering.Service.
+ setCharacteristic(this.hap.Characteristic.Name, this.WindowCovering.Name)
+ .setCharacteristic(this.hap.Characteristic.ObstructionDetected, false)
+ .getCharacteristic(this.hap.Characteristic.PositionState)
+ .onGet(() => {
+ return this.WindowCovering.PositionState;
+ });
- this.windowCoveringService
+ // Initialize WindowCovering CurrentPosition
+ this.WindowCovering.Service
.getCharacteristic(this.hap.Characteristic.CurrentPosition)
.setProps({
- minStep: this.minStep(device),
+ minStep: device.curtain?.set_minStep ?? 1,
minValue: 0,
maxValue: 100,
validValueRanges: [0, 100],
})
.onGet(() => {
- return this.CurrentPosition;
+ return this.WindowCovering.CurrentPosition;
});
- this.windowCoveringService
+ // Initialize WindowCovering TargetPosition
+ this.WindowCovering.Service
.getCharacteristic(this.hap.Characteristic.TargetPosition)
.setProps({
- minStep: this.minStep(device),
+ minStep: device.curtain?.set_minStep ?? 1,
minValue: 0,
maxValue: 100,
validValueRanges: [0, 100],
})
+ .onGet(() => {
+ return this.WindowCovering.TargetPosition;
+ })
.onSet(this.TargetPositionSet.bind(this));
- this.windowCoveringService
+ // Initialize WindowCovering TargetPosition
+ this.WindowCovering.Service
.getCharacteristic(this.hap.Characteristic.HoldPosition)
+ .onGet(() => {
+ return this.WindowCovering.HoldPosition;
+ })
.onSet(this.HoldPositionSet.bind(this));
- // Light Sensor Service
- if (device.curtain?.hide_lightsensor) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`);
- this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor);
- accessory.removeService(this.lightSensorService!);
- } else if (!this.lightSensorService) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`);
- const lightSensorService = `${accessory.displayName} Light Sensor`;
- (this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor)
- || this.accessory.addService(this.hap.Service.LightSensor)), lightSensorService;
+ // Initialize Battery Service
+ accessory.context.Battery = accessory.context.Battery ?? {};
+ this.Battery = {
+ Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`,
+ Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service,
+ BatteryLevel: accessory.context.BatteryLevel ?? 100,
+ StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL,
+ ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING,
+ };
+ accessory.context.Battery = this.Battery as object;
+
+ // Initialize Battery Service
+ this.Battery.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name)
+ .getCharacteristic(this.hap.Characteristic.BatteryLevel)
+ .onGet(() => {
+ return this.Battery.BatteryLevel;
+ });
- this.lightSensorService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Light Sensor`);
- if (!this.lightSensorService?.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.lightSensorService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Light Sensor`);
- }
- } else {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Light Sensor Service Not Added`);
- }
+ this.Battery.Service
+ .getCharacteristic(this.hap.Characteristic.StatusLowBattery)
+ .onGet(() => {
+ return this.Battery.StatusLowBattery;
+ });
- // Battery Service
- const batteryService = `${accessory.displayName} Battery`;
- (this.batteryService = this.accessory.getService(this.hap.Service.Battery)
- || accessory.addService(this.hap.Service.Battery)), batteryService;
+ this.Battery.Service
+ .getCharacteristic(this.hap.Characteristic.ChargingState)
+ .onGet(() => {
+ return this.Battery.ChargingState;
+ });
- this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`);
- if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`);
+ // Initialize LightSensor Service
+ if (device.curtain?.hide_lightsensor) {
+ if (this.LightSensor?.Service) {
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`);
+ this.LightSensor.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service;
+ accessory.removeService(this.LightSensor.Service);
+ accessory.context.LightSensor = {};
+ }
+ } else {
+ accessory.context.LightSensor = accessory.context.LightSensor ?? {};
+ this.LightSensor = {
+ Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`,
+ Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service,
+ CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001,
+ };
+ accessory.context.LightSensor = this.LightSensor as object;
+
+ // Initialize LightSensor Characteristic
+ this.LightSensor.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.LightSensor.Name)
+ .setCharacteristic(this.hap.Characteristic.StatusActive, true)
+ .getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel)
+ .onGet(() => {
+ return this.LightSensor!.CurrentAmbientLightLevel!;
+ });
}
+ // Retrieve initial values and updateHomekit
+ this.refreshStatus();
+
// Update Homekit
this.updateHomeKitCharacteristics();
@@ -199,35 +187,17 @@ export class Curtain {
});
//regisiter webhook event handler
- if (this.device.webhook) {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`);
- this.platform.webhookEventHandler[this.device.deviceId] = async (context) => {
- try {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
- const { slidePosition, battery } = context;
- const { CurrentPosition, BatteryLevel } = this;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` +
- '(slidePosition, battery) = ' +
- `Webhook:(${slidePosition}, ${battery}), ` +
- `current:(${CurrentPosition}, ${BatteryLevel})`);
- this.CurrentPosition = slidePosition;
- this.BatteryLevel = battery;
- this.updateHomeKitCharacteristics();
- } catch (e: any) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
- + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
- }
- };
- }
+ this.registerWebhook(accessory, device);
// update slide progress
- interval(this.updateRate * 1000)
+ interval(this.deviceUpdateRate * 1000)
//.pipe(skipWhile(() => this.curtainUpdateInProgress))
.subscribe(async () => {
- if (this.PositionState === this.hap.Characteristic.PositionState.STOPPED) {
+ if (this.WindowCovering.PositionState === this.hap.Characteristic.PositionState.STOPPED) {
return;
}
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Refresh Status When Moving, PositionState: ${this.PositionState}`);
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Refresh Status When Moving,`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
await this.refreshStatus();
});
@@ -238,29 +208,37 @@ export class Curtain {
tap(() => {
this.curtainUpdateInProgress = true;
}),
- debounceTime(this.platform.config.options!.pushRate! * 1000),
+ debounceTime(this.devicePushRate * 1000),
)
.subscribe(async () => {
try {
await this.pushChanges();
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` +
- ` Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,`
+ + ` Error Message: ${JSON.stringify(e.message)}`);
}
this.curtainUpdateInProgress = false;
});
// Setup EVE history features
- this.setupHistoryService(device);
+ this.setupHistoryService(accessory, device);
+ }
+
+ private history(device: device & devicesConfig) {
+ if (device.history === true) {
+ // initialize when this accessory is newly created.
+ this.accessory.context.lastActivation = this.accessory.context.lastActivation ?? 0;
+ } else {
+ // removes cached values if history is turned off
+ delete this.accessory.context.lastActivation;
+ }
}
/*
* Setup EVE history features for curtain devices.
*/
- async setupHistoryService(device: device & devicesConfig): Promise {
+ async setupHistoryService(accessory: PlatformAccessory, device: device & devicesConfig): Promise {
if (device.history !== true) {
return;
}
@@ -295,8 +273,7 @@ export class Curtain {
this.accessory.context.lastActivation = entry.time;
sensor?.updateCharacteristic(
this.platform.eve.Characteristics.LastActivation,
- Math.max(0, this.accessory.context.lastActivation - this.historyService.getInitialTime()),
- );
+ Math.max(0, this.accessory.context.lastActivation - this.historyService.getInitialTime()));
this.historyService.addEntry(entry);
}
});
@@ -304,7 +281,7 @@ export class Curtain {
}
async updateHistory(): Promise {
- const motion = Number(this.CurrentPosition) > 0 ? 1 : 0;
+ const motion = Number(this.WindowCovering.CurrentPosition) > 0 ? 1 : 0;
this.historyService.addEntry({
time: Math.round(new Date().valueOf() / 1000),
motion: motion,
@@ -317,198 +294,200 @@ export class Curtain {
);
}
- /**
- * Parse the device status from the SwitchBot api
- */
- async parseStatus(): Promise {
- if (!this.device.enableCloudService && this.OpenAPI) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`);
- } else if (this.BLE) {
- await this.BLEparseStatus();
- } else if (this.OpenAPI && this.platform.config.credentials?.token) {
- await this.openAPIparseStatus();
- } else {
- await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`,
- );
- }
- }
-
- async BLEparseStatus(): Promise {
+ async BLEparseStatus(serviceData: serviceData): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
// CurrentPosition
- this.CurrentPosition = 100 - Number(this.BLE_CurrentPosition);
+ this.WindowCovering.CurrentPosition = 100 - Number(serviceData.position);
await this.setMinMax();
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition ${this.CurrentPosition}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition ${this.WindowCovering.CurrentPosition}`);
if (this.setNewTarget) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Checking Status ...`);
await this.setMinMax();
- if (Number(this.TargetPosition) > this.CurrentPosition) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition}`);
- this.PositionState = this.hap.Characteristic.PositionState.INCREASING;
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`);
- } else if (Number(this.TargetPosition) < this.CurrentPosition) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition}`);
- this.PositionState = this.hap.Characteristic.PositionState.DECREASING;
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`);
+ if (Number(this.WindowCovering.TargetPosition) > this.WindowCovering.CurrentPosition) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING;
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} INCREASING`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
+ } else if (Number(this.WindowCovering.TargetPosition) < this.WindowCovering.CurrentPosition) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING;
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} DECREASING`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
} else {
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} Standby, CurrentPosition: ${this.CurrentPosition}`);
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby,`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} STOPPED`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
}
} else {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`);
- this.TargetPosition = this.CurrentPosition;
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
+ this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition;
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`);
}
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` +
- ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},`
+ + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`);
if (!this.device.curtain?.hide_lightsensor) {
- this.set_minLux = this.minLux();
- this.set_maxLux = this.maxLux();
- this.spaceBetweenLevels = 9;
+ const set_minLux = this.device.curtain?.set_minLux ?? 1;
+ const set_maxLux = this.device.curtain?.set_maxLux ?? 6001;
+ const spaceBetweenLevels = 9;
// Brightness
- switch (this.BLE_CurrentAmbientLightLevel) {
+ switch (serviceData.lightLevel) {
case 1:
- this.CurrentAmbientLightLevel = this.set_minLux;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = set_minLux;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
break;
case 2:
- this.CurrentAmbientLightLevel = (this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels;
+ this.LightSensor!.CurrentAmbientLightLevel = (set_maxLux - set_minLux) / spaceBetweenLevels;
this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` +
- ` Calculation: ${(this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels}`,
- );
+ `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},`
+ + ` Calculation: ${(set_maxLux - set_minLux) / spaceBetweenLevels}`);
break;
case 3:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 2;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 2;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
break;
case 4:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 3;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 3;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
break;
case 5:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 4;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 4;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
break;
case 6:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 5;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 5;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
break;
case 7:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 6;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 6;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
break;
case 8:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 7;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 7;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
break;
case 9:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 8;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel}`);
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 8;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel}`);
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
break;
case 10:
default:
- this.CurrentAmbientLightLevel = this.set_maxLux;
+ this.LightSensor!.CurrentAmbientLightLevel = set_maxLux;
this.debugLog();
}
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.BLE_CurrentAmbientLightLevel},` +
- ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${serviceData.lightLevel},`
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
}
// Battery
- this.BatteryLevel = Number(this.BLE_BatteryLevel);
- if (this.BatteryLevel < 10) {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
+ this.Battery.BatteryLevel = Number(serviceData.battery);
+ if (this.Battery.BatteryLevel < 10) {
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
} else {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
}
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},` + ` StatusLowBattery: ${this.StatusLowBattery}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
}
- async openAPIparseStatus(): Promise {
+ async openAPIparseStatus(deviceStatus: deviceStatus): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
// CurrentPosition
- this.CurrentPosition = 100 - Number(this.OpenAPI_CurrentPosition);
+ this.WindowCovering!.CurrentPosition = 100 - Number(deviceStatus.body.slidePosition);
await this.setMinMax();
- this.debugLog(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
+ this.debugLog(`Curtain ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
if (this.setNewTarget) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Checking Status ...`);
}
- if (this.setNewTarget && this.OpenAPI_InMotion) {
+ if (this.setNewTarget && deviceStatus.body.moving) {
await this.setMinMax();
- if (Number(this.TargetPosition) > this.CurrentPosition) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.CurrentPosition} `);
- this.PositionState = this.hap.Characteristic.PositionState.INCREASING;
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} INCREASING PositionState: ${this.PositionState}`);
- } else if (Number(this.TargetPosition) < this.CurrentPosition) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.CurrentPosition} `);
- this.PositionState = this.hap.Characteristic.PositionState.DECREASING;
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} DECREASING PositionState: ${this.PositionState}`);
+ if (Number(this.WindowCovering.TargetPosition) > this.WindowCovering.CurrentPosition) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Closing, CurrentPosition: ${this.WindowCovering.CurrentPosition} `);
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING;
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} INCREASING`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
+ } else if (Number(this.WindowCovering.TargetPosition) < this.WindowCovering.CurrentPosition) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Opening, CurrentPosition: ${this.WindowCovering.CurrentPosition} `);
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING;
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} DECREASING`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
} else {
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} Standby, CurrentPosition: ${this.CurrentPosition}`);
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
- this.debugLog(`${this.device.deviceType}: ${this.CurrentPosition} STOPPED PositionState: ${this.PositionState}`);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} Standby,`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
+ this.debugLog(`${this.device.deviceType}: ${this.WindowCovering.CurrentPosition} STOPPED`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
}
} else {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.CurrentPosition}`);
- this.TargetPosition = this.CurrentPosition;
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Standby, CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
+ this.WindowCovering.TargetPosition = this.WindowCovering.CurrentPosition;
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Stopped`);
}
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition},` +
- ` TargetPosition: ${this.TargetPosition}, PositionState: ${this.PositionState},`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition},`
+ + ` TargetPosition: ${this.WindowCovering.TargetPosition}, PositionState: ${this.WindowCovering.PositionState},`);
// Brightness
if (!this.device.curtain?.hide_lightsensor) {
- this.set_minLux = this.minLux();
- this.set_maxLux = this.maxLux();
- switch (this.OpenAPI_CurrentAmbientLightLevel) {
- case 'dim':
- this.CurrentAmbientLightLevel = this.set_minLux;
- break;
+ const set_minLux = this.device.curtain?.set_minLux ?? 1;
+ const set_maxLux = this.device.curtain?.set_maxLux ?? 6001;
+ switch (deviceStatus.body.brightness) {
case 'bright':
+ this.LightSensor!.CurrentAmbientLightLevel = set_maxLux;
+ this.Battery.ChargingState = this.hap.Characteristic.ChargingState.CHARGING;
+ break;
+ case 'dim':
default:
- this.CurrentAmbientLightLevel = this.set_maxLux;
+ this.LightSensor!.CurrentAmbientLightLevel = set_minLux;
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
}
// BatteryLevel
- this.BatteryLevel = Number(this.OpenAPI_BatteryLevel);
- if (this.BatteryLevel < 10) {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
+ this.Battery.BatteryLevel = Number(deviceStatus.body.battery);
+ if (this.Battery.BatteryLevel < 10) {
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
} else {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
}
- if (Number.isNaN(this.BatteryLevel)) {
- this.BatteryLevel = 100;
+ if (Number.isNaN(this.Battery.BatteryLevel)) {
+ this.Battery.BatteryLevel = 100;
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},`
- + ` StatusLowBattery: ${this.StatusLowBattery}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
- // FirmwareRevision
- this.FirmwareRevision = this.OpenAPI_FirmwareRevision!;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
+ // Firmware Version
+ const version = deviceStatus.body.version?.toString();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
+ if (deviceStatus.body.version) {
+ const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
+ this.accessory
+ .getService(this.hap.Service.AccessoryInformation)!
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
+ .updateValue(deviceVersion);
+ this.accessory.context.deviceVersion = deviceVersion;
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
+ }
}
async refreshStatus(): Promise {
@@ -520,10 +499,8 @@ export class Curtain {
await this.openAPIRefreshStatus();
} else {
await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` +
- ` ${this.device.connectionType}, refreshStatus will not happen.`,
- );
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
}
}
@@ -538,118 +515,45 @@ export class Curtain {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
this.getCustomBLEAddress(switchbot);
// Start to monitor advertisement packets
- let model: string;
- switch (this.device.configDeviceType) {
- case 'Curtain3':
- model = '{';
- break;
- default:
- model = 'c';
- }
(async () => {
// Start to monitor advertisement packets
- await switchbot.startScan({
- model: model,
- id: this.device.bleMac,
- });
+ await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac });
// Set an event handler
switchbot.onadvertisement = (ad: any) => {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`);
- if (this.device.bleMac === ad.address && ad.serviceData.model === 'c') {
+ if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.BLE_Calibration = ad.serviceData.calibration;
- this.BLE_BatteryLevel = ad.serviceData.battery;
- this.BLE_InMotion = ad.serviceData.inMotion;
- this.BLE_CurrentPosition = ad.serviceData.position;
- this.BLE_CurrentAmbientLightLevel = ad.serviceData.lightLevel;
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
}
};
- // Wait 1 seconds
+ // Wait 10 seconds
await switchbot.wait(this.scanDuration * 1000);
// Stop to monitor
await switchbot.stopScan();
// Update HomeKit
- await this.BLEparseStatus();
+ await this.BLEparseStatus(switchbot.onadvertisement.serviceData);
await this.updateHomeKitCharacteristics();
})();
-
-
- /*if (switchbot !== false) {
- switchbot
- .startScan({
- model: model,
- id: this.device.bleMac,
- })
- .then(async () => {
- // Set an event handler
- switchbot.onadvertisement = async (ad: ad) => {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` +
- ` BLE Address Found: ${ad.address}`,
- );
- this.BLE_Calibration = ad.serviceData.calibration;
- this.BLE_BatteryLevel = ad.serviceData.battery;
- this.BLE_InMotion = ad.serviceData.inMotion;
- this.BLE_CurrentPosition = ad.serviceData.position;
- this.BLE_CurrentAmbientLightLevel = ad.serviceData.lightLevel;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} calibration: ${ad.serviceData.calibration}, ` +
- `position: ${ad.serviceData.position}, lightLevel: ${ad.serviceData.lightLevel}, battery: ${ad.serviceData.battery}, ` +
- `inMotion: ${ad.serviceData.inMotion}`,
- );
-
- if (ad.serviceData) {
- this.BLE_IsConnected = true;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
- await this.stopScanning(switchbot);
- } else {
- this.BLE_IsConnected = false;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
- }
- };
- // Wait
- return await sleep(this.scanDuration * 1000);
- })
- .then(async () => {
- // Stop to monitor
- await this.stopScanning(switchbot);
- })
- .catch(async (e: any) => {
- this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
- await this.BLERefreshConnection(switchbot);
- });
- } else {
+ if (switchbot === undefined) {
await this.BLERefreshConnection(switchbot);
- }*/
+ }
}
async openAPIRefreshStatus(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
try {
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, {
- headers: this.platform.generateHeaders(),
- });
+ const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries,
+ `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() });
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
const deviceStatus: any = await body.json();
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
- this.OpenAPI_CurrentPosition = deviceStatus.body.slidePosition;
- this.OpenAPI_InMotion = deviceStatus.body.moving;
- this.OpenAPI_CurrentAmbientLightLevel = deviceStatus.body.brightness;
- this.OpenAPI_BatteryLevel = deviceStatus.body.battery;
- this.OpenAPI_FirmwareRevision = deviceStatus.body.version;
- this.openAPIparseStatus();
+ this.openAPIparseStatus(deviceStatus);
this.updateHomeKitCharacteristics();
} else {
this.statusCode(statusCode);
@@ -657,10 +561,30 @@ export class Curtain {
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
+ }
+ }
+
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
+ if (device.webhook) {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`);
+ this.platform.webhookEventHandler[device.deviceId] = async (context) => {
+ try {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
+ const { slidePosition, battery } = context;
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} ` +
+ '(slidePosition, battery) = ' +
+ `Webhook:(${slidePosition}, ${battery}), `
+ + `current:(${this.WindowCovering.CurrentPosition}, ${this.Battery.BatteryLevel})`);
+ this.WindowCovering.CurrentPosition = slidePosition;
+ this.Battery.BatteryLevel = battery;
+ this.updateHomeKitCharacteristics();
+ } catch (e: any) {
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} `
+ + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
+ }
+ };
}
}
@@ -673,9 +597,8 @@ export class Curtain {
await this.openAPIpushChanges();
} else {
await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`,
- );
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, pushChanges will not happen.`);
}
// Refresh the status from the API
interval(15000)
@@ -688,7 +611,7 @@ export class Curtain {
async BLEpushChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`);
- if (this.TargetPosition !== this.CurrentPosition) {
+ if (this.WindowCovering.TargetPosition !== this.WindowCovering.CurrentPosition) {
const switchbot = await this.platform.connectBLE();
// Convert to BLE Address
this.device.bleMac = this.device
@@ -696,28 +619,28 @@ export class Curtain {
.join(':')
.toLowerCase();
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
- this.SilentPerformance();
- const adjustedMode = this.setPositionMode === '1' ? 0x01 : 0xff;
- this.debugLog(`${this.accessory.displayName} Mode: ${this.Mode}`);
+ const { setPositionMode, Mode }: { setPositionMode: number; Mode: string; } = await this.setPerformance();
+ const adjustedMode = setPositionMode === 1 ? 0x01 : 0xff;
+ this.debugLog(`${this.accessory.displayName} Mode: ${Mode}`);
if (switchbot !== false) {
try {
const device_list = await switchbot.discover({ model: 'c', quick: true, id: this.device.bleMac });
- this.infoLog(`${this.accessory.displayName} Target Position: ${this.TargetPosition}`);
+ this.infoLog(`${this.accessory.displayName} Target Position: ${this.WindowCovering.TargetPosition}`);
- await this.retry({
- max: this.maxRetry(),
+ await this.retryBLE({
+ max: await this.maxRetryBLE(),
fn: async () => {
- await device_list[0].runToPos(100 - Number(this.TargetPosition), adjustedMode);
+ await device_list[0].runToPos(100 - Number(this.WindowCovering.TargetPosition), adjustedMode);
},
});
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `TargetPosition: ${this.WindowCovering.TargetPosition} sent over BLE, sent successfully`);
} catch (e) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify((e as Error).message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify((e as Error).message)}`);
await this.BLEPushConnection();
throw new Error('Connection error');
}
@@ -726,30 +649,8 @@ export class Curtain {
await this.BLEPushConnection();
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.` +
- ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`,
- );
- }
- }
-
- async retry({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise {
- return fn().catch(async (e: any) => {
- if (max === 0) {
- throw e;
- }
- this.infoLog(e);
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`);
- await sleep(1000);
- return this.retry({ max: max - 1, fn });
- });
- }
-
- maxRetry(): number {
- if (this.device.maxRetry) {
- return this.device.maxRetry;
- } else {
- return 5;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges, CurrentPosition & TargetPosition Are the Same.`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`);
}
}
@@ -758,24 +659,13 @@ export class Curtain {
let parameter: string;
let commandType: string;
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`);
- if (this.TargetPosition !== this.CurrentPosition || this.device.disableCaching) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing ${this.TargetPosition}`);
- const adjustedTargetPosition = 100 - Number(this.TargetPosition);
- if (Number(this.TargetPosition) > 50) {
- this.setPositionMode = this.device.curtain?.setOpenMode;
- } else {
- this.setPositionMode = this.device.curtain?.setCloseMode;
- }
- if (this.setPositionMode === '1') {
- this.Mode = 'Silent Mode';
- } else if (this.setPositionMode === '0') {
- this.Mode = 'Performance Mode';
- } else {
- this.Mode = 'Default Mode';
- }
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Mode: ${this.Mode}`);
- const adjustedMode = this.setPositionMode || 'ff';
- if (this.HoldPosition) {
+ if (this.WindowCovering.TargetPosition !== this.WindowCovering.CurrentPosition || this.device.disableCaching) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing ${this.WindowCovering.TargetPosition}`);
+ const adjustedTargetPosition = 100 - Number(this.WindowCovering.TargetPosition);
+ const { setPositionMode, Mode }: { setPositionMode: number; Mode: string; } = await this.setPerformance();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Mode: ${Mode}`);
+ const adjustedMode = setPositionMode || 'ff';
+ if (this.WindowCovering.HoldPosition) {
command = 'pause';
parameter = 'default';
commandType = 'command';
@@ -804,22 +694,20 @@ export class Curtain {
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
} else {
this.statusCode(statusCode);
this.statusCode(deviceStatus.statusCode);
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.` +
- ` CurrentPosition: ${this.CurrentPosition}, TargetPosition ${this.TargetPosition}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No OpenAPI Changes, CurrentPosition & TargetPosition Are the Same.`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}, TargetPosition ${this.WindowCovering.TargetPosition}`);
}
}
@@ -827,50 +715,53 @@ export class Curtain {
* Handle requests to set the value of the "Target Position" characteristic
*/
async TargetPositionSet(value: CharacteristicValue): Promise {
- if (this.TargetPosition === this.accessory.context.TargetPosition) {
+ if (this.WindowCovering.TargetPosition === this.accessory.context.TargetPosition) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set TargetPosition: ${value}`);
} else {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetPosition: ${value}`);
}
// Set HoldPosition to false when TargetPosition is changed
- this.HoldPosition = false;
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.HoldPosition);
+ this.WindowCovering.HoldPosition = false;
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.WindowCovering.HoldPosition);
- this.TargetPosition = value;
+ this.WindowCovering.TargetPosition = value;
if (this.device.mqttURL) {
- this.mqttPublish('TargetPosition', this.TargetPosition);
- this.mqttPublish('HoldPosition', this.HoldPosition);
+ this.mqttPublish('TargetPosition', this.WindowCovering.TargetPosition.toString());
+ this.mqttPublish('HoldPosition', this.WindowCovering.HoldPosition.toString()); // Convert boolean to string
}
await this.setMinMax();
- if (value > this.CurrentPosition) {
- this.PositionState = this.hap.Characteristic.PositionState.INCREASING;
+ if (value > this.WindowCovering.CurrentPosition) {
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.INCREASING;
this.setNewTarget = true;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
- } else if (value < this.CurrentPosition) {
- this.PositionState = this.hap.Characteristic.PositionState.DECREASING;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
+ } else if (value < this.WindowCovering.CurrentPosition) {
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.DECREASING;
this.setNewTarget = true;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
} else {
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
+ this.WindowCovering.PositionState = this.hap.Characteristic.PositionState.STOPPED;
this.setNewTarget = false;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value}, CurrentPosition: ${this.CurrentPosition}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} value: ${value},`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
}
- this.windowCoveringService.setCharacteristic(this.hap.Characteristic.PositionState, this.PositionState);
- this.windowCoveringService.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.PositionState);
+ this.WindowCovering.Service.setCharacteristic(this.hap.Characteristic.PositionState, this.WindowCovering.PositionState);
+ this.WindowCovering.Service.getCharacteristic(this.hap.Characteristic.PositionState).updateValue(this.WindowCovering.PositionState);
/**
* If Curtain movement time is short, the moving flag from backend is always false.
* The minimum time depends on the network control latency.
*/
clearTimeout(this.setNewTargetTimer);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateRate: ${this.updateRate}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateRate: ${this.deviceUpdateRate}`);
if (this.setNewTarget) {
this.setNewTargetTimer = setTimeout(() => {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} setNewTarget ${this.setNewTarget} timeout`);
this.setNewTarget = false;
- }, this.updateRate * 1000);
+ }, this.deviceUpdateRate * 1000);
}
this.doCurtainUpdate.next();
}
@@ -880,153 +771,107 @@ export class Curtain {
*/
async HoldPositionSet(value: CharacteristicValue): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} HoldPosition: ${value}`);
- this.HoldPosition = value;
+ this.WindowCovering.HoldPosition = value;
this.doCurtainUpdate.next();
}
async updateHomeKitCharacteristics(): Promise {
await this.setMinMax();
- if (this.CurrentPosition === undefined || Number.isNaN(this.CurrentPosition)) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.CurrentPosition}`);
+ if (this.WindowCovering.CurrentPosition === undefined || Number.isNaN(this.WindowCovering.CurrentPosition)) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
} else {
+ this.accessory.context.CurrentPosition = this.WindowCovering.CurrentPosition;
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, Number(this.WindowCovering.CurrentPosition));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` CurrentPosition: ${this.WindowCovering.CurrentPosition}`);
if (this.device.mqttURL) {
- this.mqttPublish('CurrentPosition', this.CurrentPosition);
+ this.mqttPublish('CurrentPosition', this.WindowCovering.CurrentPosition.toString()); // Convert to string
}
- this.accessory.context.CurrentPosition = this.CurrentPosition;
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.CurrentPosition, Number(this.CurrentPosition));
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentPosition: ${this.CurrentPosition}`);
}
- if (this.PositionState === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} PositionState: ${this.PositionState}`);
+ if (this.WindowCovering.PositionState === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} PositionState: ${this.WindowCovering.PositionState}`);
} else {
if (this.device.mqttURL) {
- this.mqttPublish('PositionState', this.PositionState);
+ this.mqttPublish('PositionState', this.WindowCovering.PositionState.toString());
}
- this.accessory.context.PositionState = this.PositionState;
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.PositionState, Number(this.PositionState));
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic PositionState: ${this.PositionState}`);
+ this.accessory.context.PositionState = this.WindowCovering.PositionState;
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, Number(this.WindowCovering.PositionState));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` PositionState: ${this.WindowCovering.PositionState}`);
}
- if (this.TargetPosition === undefined || Number.isNaN(this.TargetPosition)) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.TargetPosition}`);
+ if (this.WindowCovering.TargetPosition === undefined || Number.isNaN(this.WindowCovering.TargetPosition)) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} TargetPosition: ${this.WindowCovering.TargetPosition}`);
} else {
if (this.device.mqttURL) {
- this.mqttPublish('TargetPosition', this.TargetPosition);
+ this.mqttPublish('TargetPosition', this.WindowCovering.TargetPosition.toString());
}
- this.accessory.context.TargetPosition = this.TargetPosition;
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.TargetPosition, Number(this.TargetPosition));
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic TargetPosition: ${this.TargetPosition}`);
+ this.accessory.context.TargetPosition = this.WindowCovering.TargetPosition;
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, Number(this.WindowCovering.TargetPosition));
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` TargetPosition: ${this.WindowCovering.TargetPosition}`);
}
- if (this.HoldPosition === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} HoldPosition: ${this.HoldPosition}`);
+ if (this.WindowCovering.HoldPosition === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} HoldPosition: ${this.WindowCovering.HoldPosition}`);
} else {
if (this.device.mqttURL) {
- this.mqttPublish('HoldPosition', this.HoldPosition);
+ this.mqttPublish('HoldPosition', this.WindowCovering.HoldPosition.toString());
}
- this.accessory.context.HoldPosition = this.HoldPosition;
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.HoldPosition);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic HoldPosition: ${this.HoldPosition}`);
+ this.accessory.context.HoldPosition = this.WindowCovering.HoldPosition;
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.HoldPosition, this.WindowCovering.HoldPosition);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` HoldPosition: ${this.WindowCovering.HoldPosition}`);
}
if (!this.device.curtain?.hide_lightsensor) {
- if (this.CurrentAmbientLightLevel === undefined || Number.isNaN(this.CurrentAmbientLightLevel)) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
+ if (this.LightSensor!.CurrentAmbientLightLevel === undefined || Number.isNaN(this.LightSensor!.CurrentAmbientLightLevel)) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
} else {
if (this.device.mqttURL) {
- this.mqttPublish('CurrentAmbientLightLevel', this.CurrentAmbientLightLevel);
+ this.mqttPublish('CurrentAmbientLightLevel', this.LightSensor!.CurrentAmbientLightLevel.toString());
}
- this.accessory.context.CurrentAmbientLightLevel = this.CurrentAmbientLightLevel;
- this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` +
- ` updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
- );
+ this.accessory.context.CurrentAmbientLightLevel = this.LightSensor!.CurrentAmbientLightLevel;
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
if (this.device.history) {
this.historyService?.addEntry({
time: Math.round(new Date().valueOf() / 1000),
- lux: this.CurrentAmbientLightLevel,
+ lux: this.LightSensor!.CurrentAmbientLightLevel,
});
}
}
}
- if (this.BatteryLevel === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
+ if (this.Battery.BatteryLevel === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`);
} else {
if (this.device.mqttURL) {
- this.mqttPublish('BatteryLevel', this.BatteryLevel);
+ this.mqttPublish('BatteryLevel', this.Battery.BatteryLevel.toString());
}
- this.accessory.context.BatteryLevel = this.BatteryLevel;
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
+ this.accessory.context.BatteryLevel = this.Battery.BatteryLevel;
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`);
}
- if (this.StatusLowBattery === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
+ if (this.Battery.StatusLowBattery === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`);
} else {
if (this.device.mqttURL) {
- this.mqttPublish('StatusLowBattery', this.StatusLowBattery);
- }
- this.accessory.context.StatusLowBattery = this.StatusLowBattery;
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
- }
- }
-
- /*
- * Publish MQTT message for topics of
- * 'homebridge-switchbot/curtain/xx:xx:xx:xx:xx:xx'
- */
- mqttPublish(topic: string, message: any) {
- const mac = this.device.deviceId
- ?.toLowerCase()
- .match(/[\s\S]{1,2}/g)
- ?.join(':');
- const options = this.device.mqttPubOptions || {};
- this.mqttClient?.publish(`homebridge-switchbot/curtain/${mac}/${topic}`, `${message}`, options);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${topic}/${message} options:${JSON.stringify(options)}`);
- }
-
- /*
- * Setup MQTT hadler if URL is specified.
- */
- async setupMqtt(device: device & devicesConfig): Promise {
- if (device.mqttURL) {
- try {
- const { connectAsync } = asyncmqtt;
- this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`);
- this.mqttClient.on('error', (e: Error) => {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`);
- });
- } catch (e) {
- this.mqttClient = null;
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`);
+ this.mqttPublish('StatusLowBattery', this.Battery.StatusLowBattery.toString());
}
+ this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery;
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
}
- }
-
- async stopScanning(switchbot: any) {
- switchbot.stopScan();
- if (this.BLE_IsConnected) {
- await this.BLEparseStatus();
- await this.updateHomeKitCharacteristics();
+ if (this.Battery.ChargingState === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ChargingState: ${this.Battery.ChargingState}`);
} else {
- await this.BLERefreshConnection(switchbot);
- }
- }
-
- async getCustomBLEAddress(switchbot: any) {
- if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
- (async () => {
- // Start to monitor advertisement packets
- await switchbot.startScan({
- model: 'c',
- });
- // Set an event handler
- switchbot.onadvertisement = (ad: any) => {
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
- };
- await sleep(10000);
- // Stop to monitor
- switchbot.stopScan();
- })();
+ if (this.device.mqttURL) {
+ this.mqttPublish('ChargingState', this.Battery.ChargingState.toString());
+ }
+ this.accessory.context.ChargingState = this.Battery.ChargingState;
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, this.Battery.ChargingState);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` ChargingState: ${this.Battery.ChargingState}`);
}
}
@@ -1046,364 +891,71 @@ export class Curtain {
}
}
- async SilentPerformance() {
- if (Number(this.TargetPosition) > 50) {
+ async setPerformance() {
+ let setPositionMode: number;
+ let Mode: string;
+ if (Number(this.WindowCovering.TargetPosition) > 50) {
if (this.device.curtain?.setOpenMode === '1') {
- this.setPositionMode = '1';
- this.Mode = 'Silent Mode';
+ setPositionMode = 1;
+ Mode = 'Silent Mode';
+ } else if (this.device.curtain?.setOpenMode === '0') {
+ setPositionMode = 0;
+ Mode = 'Performance Mode';
} else {
- this.setPositionMode = '0';
- this.Mode = 'Performance Mode';
+ setPositionMode = 0;
+ Mode = 'Default Mode';
}
} else {
if (this.device.curtain?.setCloseMode === '1') {
- this.setPositionMode = '1';
- this.Mode = 'Silent Mode';
+ setPositionMode = 1;
+ Mode = 'Silent Mode';
+ } else if (this.device.curtain?.setOpenMode === '0') {
+ setPositionMode = 0;
+ Mode = 'Performance Mode';
} else {
- this.setPositionMode = '0';
- this.Mode = 'Performance Mode';
+ setPositionMode = 0;
+ Mode = 'Default Mode';
}
}
+ return { setPositionMode, Mode };
}
async setMinMax(): Promise {
if (this.device.curtain?.set_min) {
- if (Number(this.CurrentPosition) <= this.device.curtain?.set_min) {
- this.CurrentPosition = 0;
+ if (Number(this.WindowCovering.CurrentPosition) <= this.device.curtain?.set_min) {
+ this.WindowCovering.CurrentPosition = 0;
}
}
if (this.device.curtain?.set_max) {
- if (Number(this.CurrentPosition) >= this.device.curtain?.set_max) {
- this.CurrentPosition = 100;
+ if (Number(this.WindowCovering.CurrentPosition) >= this.device.curtain?.set_max) {
+ this.WindowCovering.CurrentPosition = 100;
}
}
if (this.device.history) {
const motion = this.accessory.getService(this.hap.Service.MotionSensor);
- const state = Number(this.CurrentPosition) > 0 ? 1 : 0;
+ const state = Number(this.WindowCovering.CurrentPosition) > 0 ? 1 : 0;
motion?.updateCharacteristic(this.hap.Characteristic.MotionDetected, state);
}
}
- minStep(device: device & devicesConfig): number {
- if (device.curtain?.set_minStep) {
- this.set_minStep = device.curtain?.set_minStep;
- } else {
- this.set_minStep = 1;
- }
- return this.set_minStep;
- }
-
- minLux(): number {
- if (this.device.curtain?.set_minLux) {
- this.set_minLux = this.device.curtain?.set_minLux;
- } else {
- this.set_minLux = 1;
- }
- return this.set_minLux;
- }
-
- maxLux(): number {
- if (this.device.curtain?.set_maxLux) {
- this.set_maxLux = this.device.curtain?.set_maxLux;
- } else {
- this.set_maxLux = 6001;
- }
- return this.set_maxLux;
- }
-
- async scan(device: device & devicesConfig): Promise {
- if (device.scanDuration) {
- if (this.updateRate > device.scanDuration) {
- this.scanDuration = this.updateRate;
- if (this.BLE) {
- this.warnLog(
- `${this.device.deviceType}: ` +
- `${this.accessory.displayName} scanDuration is less than updateRate, overriding scanDuration with updateRate`,
- );
- }
- } else {
- this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
- }
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
- }
- } else {
- if (this.updateRate > 1) {
- this.scanDuration = this.updateRate;
- } else {
- this.scanDuration = this.accessory.context.scanDuration = 1;
- }
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
- }
- }
- }
-
- async statusCode(statusCode: number): Promise {
- switch (statusCode) {
- case 151:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
- break;
- case 152:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
- break;
- case 160:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
- break;
- case 161:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
- this.offlineOff();
- break;
- case 171:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` +
- `Hub: ${this.device.hubDeviceId}`,
- );
- this.offlineOff();
- break;
- case 190:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
- ` Or command format is invalid, statusCode: ${statusCode}`,
- );
- break;
- case 100:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
- break;
- case 200:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
- break;
- case 400:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. `
- + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`);
- break;
- case 401:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, `
- + `but the request has not been authenticated, statusCode: ${statusCode}`);
- break;
- case 403:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not `
- + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`);
- break;
- case 404:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, `
- + `statusCode: ${statusCode}`);
- break;
- case 406:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via `
- + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`);
- break;
- case 415:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType `
- + `header that is not supported by the server, statusCode: ${statusCode}`);
- break;
- case 422:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, The client has made a valid request, `
- + `but the server cannot process it. This is often used for APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
- break;
- case 429:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of `
- + `requests allowed for a given time window, statusCode: ${statusCode}`);
- break;
- case 500:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings `
- + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`);
- break;
- default:
- this.infoLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` +
- `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`,
- );
- }
- }
-
async offlineOff(): Promise {
if (this.device.offline) {
- await this.deviceContext();
- await this.updateHomeKitCharacteristics();
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, 100);
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, 100);
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, this.hap.Characteristic.PositionState.STOPPED);
}
}
async apiError(e: any): Promise {
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e);
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.PositionState, e);
- this.windowCoveringService.updateCharacteristic(this.hap.Characteristic.TargetPosition, e);
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.CurrentPosition, e);
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.PositionState, e);
+ this.WindowCovering.Service.updateCharacteristic(this.hap.Characteristic.TargetPosition, e);
+ 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.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e);
- }
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e);
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e);
- //throw new this.platform.api.hap.HapStatusError(HAPStatus.SERVICE_COMMUNICATION_FAILURE);
- }
-
- async deviceContext() {
- if (this.accessory.context.CurrentPosition === undefined) {
- this.CurrentPosition = 0;
- } else {
- this.CurrentPosition = this.accessory.context.CurrentPosition;
- }
-
- if (this.accessory.context.TargetPosition === undefined) {
- this.TargetPosition = 0;
- } else {
- this.TargetPosition = this.accessory.context.TargetPosition;
- }
-
- if (this.accessory.context.PositionState === undefined) {
- this.PositionState = this.hap.Characteristic.PositionState.STOPPED;
- } else {
- this.PositionState = this.accessory.context.PositionState;
- }
-
- if (!this.device.curtain?.hide_lightsensor) {
- if (this.accessory.context.CurrentAmbientLightLevel !== undefined) {
- this.CurrentAmbientLightLevel = this.accessory.context.CurrentAmbientLightLevel;
- }
- }
-
- if (this.BLE) {
- if (this.accessory.context.BatteryLevel === undefined) {
- this.BatteryLevel = 100;
- } else {
- this.BatteryLevel = this.accessory.context.BatteryLevel;
- }
- if (this.accessory.context.StatusLowBattery === undefined) {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
- } else {
- this.StatusLowBattery = this.accessory.context.StatusLowBattery;
- }
- }
-
- if (this.device.history === true) {
- // initialize when this accessory is newly created.
- this.accessory.context.lastActivation = this.accessory.context.lastActivation ?? 0;
- } else {
- // removes cached values if history is turned off
- delete this.accessory.context.lastActivation;
- }
- if (this.FirmwareRevision === undefined) {
- this.FirmwareRevision = this.platform.version;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
- }
- }
-
- async refreshRate(device: device & devicesConfig): Promise {
- // refreshRate
- if (device.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
- } else if (this.platform.config.options!.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e);
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.StatusActive, e);
}
- // updateRate
- if (device?.curtain?.updateRate) {
- this.updateRate = device?.curtain?.updateRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Curtain updateRate: ${this.updateRate}`);
- } else {
- this.updateRate = 7;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default Curtain updateRate: ${this.updateRate}`);
- }
- }
-
- async deviceConfig(device: device & devicesConfig): Promise {
- let config = {};
- if (device.curtain) {
- config = device.curtain;
- }
- if (device.connectionType !== undefined) {
- config['connectionType'] = device.connectionType;
- }
- if (device.external !== undefined) {
- config['external'] = device.external;
- }
- if (device.mqttURL !== undefined) {
- config['mqttURL'] = device.mqttURL;
- }
- if (device.logging !== undefined) {
- config['logging'] = device.logging;
- }
- if (device.refreshRate !== undefined) {
- config['refreshRate'] = device.refreshRate;
- }
- if (device.scanDuration !== undefined) {
- config['scanDuration'] = device.scanDuration;
- }
- if (device.maxRetry !== undefined) {
- config['maxRetry'] = device.maxRetry;
- }
- if (device.webhook !== undefined) {
- config['webhook'] = device.webhook;
- }
- if (Object.entries(config).length !== 0) {
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
- }
- }
-
- async deviceLogs(device: device & devicesConfig): Promise {
- if (this.platform.debugMode) {
- this.deviceLogging = this.accessory.context.logging = 'debugMode';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
- } else if (device.logging) {
- this.deviceLogging = this.accessory.context.logging = device.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
- } else if (this.platform.config.options?.logging) {
- this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
- } else {
- this.deviceLogging = this.accessory.context.logging = 'standard';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
- }
- }
-
- /**
- * Logging for Device
- */
- infoLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.info(String(...log));
- }
- }
-
- warnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.warn(String(...log));
- }
- }
-
- debugWarnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.warn('[DEBUG]', String(...log));
- }
- }
- }
-
- errorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.error(String(...log));
- }
- }
-
- debugErrorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.error('[DEBUG]', String(...log));
- }
- }
- }
-
- debugLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging === 'debug') {
- this.platform.log.info('[DEBUG]', String(...log));
- } else {
- this.platform.log.debug(String(...log));
- }
- }
- }
-
- enablingDeviceLogging(): boolean {
- return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard';
}
}
diff --git a/src/device/device.ts b/src/device/device.ts
new file mode 100644
index 00000000..90d8b1b6
--- /dev/null
+++ b/src/device/device.ts
@@ -0,0 +1,755 @@
+/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+ *
+ * device.ts: @switchbot/homebridge-switchbot.
+ */
+
+import { hostname } from 'os';
+import asyncmqtt from 'async-mqtt';
+import { BlindTiltMappingMode, SwitchBotModel, SwitchBotBLEModel, sleep } from '../utils.js';
+
+import type { MqttClient } from 'mqtt';
+import type { SwitchBotPlatform } from '../platform.js';
+import type { API, HAP, Logging, PlatformAccessory } from 'homebridge';
+import type { SwitchBotPlatformConfig, device, devicesConfig } from '../settings.js';
+
+export abstract class deviceBase {
+ public readonly api: API;
+ public readonly log: Logging;
+ public readonly config!: SwitchBotPlatformConfig;
+ protected readonly hap: HAP;
+
+ // Config
+ protected deviceLogging!: string;
+ protected deviceRefreshRate!: number;
+ protected deviceUpdateRate!: number;
+ protected devicePushRate!: number;
+ protected deviceMaxRetries!: number;
+ protected deviceDelayBetweenRetries!: number;
+
+ // Connection
+ protected readonly BLE: boolean;
+ protected readonly OpenAPI: boolean;
+
+ // Accsrroy Information
+ protected deviceModel!: SwitchBotModel;
+ protected deviceBLEModel!: SwitchBotBLEModel;
+
+ // BLE
+ protected scanDuration!: number;
+
+ // EVE history service handler
+ protected historyService?: any = null;
+
+ //MQTT stuff
+ protected mqttClient: MqttClient | null = null;
+
+ constructor(
+ protected readonly platform: SwitchBotPlatform,
+ protected accessory: PlatformAccessory,
+ protected device: device & devicesConfig,
+ ) {
+ this.api = this.platform.api;
+ this.log = this.platform.log;
+ this.config = this.platform.config;
+ this.hap = this.api.hap;
+
+ // Connection
+ this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI';
+ this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI';
+
+ this.getDeviceLogSettings(accessory, device);
+ this.getDeviceRateSettings(accessory, device);
+ this.getDeviceRetry(accessory, device);
+ this.getDeviceConfigSettings(accessory, device);
+ this.getDeviceContext(accessory, device);
+ this.setupMqtt(accessory, device);
+ this.scan(accessory, device);
+
+ // Set accessory information
+ accessory
+ .getService(this.hap.Service.AccessoryInformation)!
+ .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
+ .setCharacteristic(this.hap.Characteristic.AppMatchingIdentifier, 'id1087374760')
+ .setCharacteristic(this.hap.Characteristic.Name, device.deviceName ?? accessory.displayName)
+ .setCharacteristic(this.hap.Characteristic.ConfiguredName, device.deviceName ?? accessory.displayName)
+ .setCharacteristic(this.hap.Characteristic.Model, device.model ?? accessory.context.model)
+ .setCharacteristic(this.hap.Characteristic.ProductData, device.deviceId ?? accessory.context.deviceId)
+ .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId);
+ }
+
+ async getDeviceLogSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise {
+ if (this.platform.debugMode) {
+ this.deviceLogging = accessory.context.logging = 'debugMode';
+ this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
+ } else if (device.logging) {
+ this.deviceLogging = accessory.context.logging = device.logging;
+ this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
+ } else if (this.config.logging) {
+ this.deviceLogging = accessory.context.logging = this.config.logging;
+ this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
+ } else {
+ this.deviceLogging = accessory.context.logging = 'standard';
+ this.debugWarnLog(`${this.device.deviceType}: ${accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
+ }
+ }
+
+ async getDeviceRateSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise {
+ // refreshRate
+ if (device.refreshRate) {
+ this.deviceRefreshRate = device.refreshRate;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
+ } else if (this.config.options?.refreshRate) {
+ this.deviceRefreshRate = this.config.options.refreshRate;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
+ } else {
+ this.deviceRefreshRate = 5;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Default refreshRate: ${this.deviceRefreshRate}`);
+ }
+ accessory.context.deviceRefreshRate = this.deviceRefreshRate;
+ // updateRate
+ if (device.updateRate) {
+ this.deviceUpdateRate = device.updateRate;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Device Config updateRate: ${this.deviceUpdateRate}`);
+ } else if (this.config.options?.updateRate) {
+ this.deviceUpdateRate = this.config.options.updateRate;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Platform Config updateRate: ${this.deviceUpdateRate}`);
+ } else {
+ this.deviceUpdateRate = 5;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Default updateRate: ${this.deviceUpdateRate}`);
+ }
+ accessory.context.deviceUpdateRate = this.deviceUpdateRate;
+ // pushRate
+ if (device.pushRate) {
+ this.devicePushRate = device.pushRate;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Device Config pushRate: ${this.deviceUpdateRate}`);
+ } else if (this.config.options?.pushRate) {
+ this.devicePushRate = this.config.options.pushRate;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Platform Config pushRate: ${this.deviceUpdateRate}`);
+ } else {
+ this.devicePushRate = 1;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Default pushRate: ${this.deviceUpdateRate}`);
+ }
+ accessory.context.devicePushRate = this.devicePushRate;
+ }
+
+ async getDeviceRetry(accessory: PlatformAccessory, device: device & devicesConfig): Promise {
+ if (device.maxRetries) {
+ this.deviceMaxRetries = device.maxRetries;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Device Max Retries: ${this.deviceMaxRetries}`);
+ } else {
+ this.deviceMaxRetries = 5; // Maximum number of retries
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Max Retries Not Set, Using: ${this.deviceMaxRetries}`);
+ }
+ if (device.delayBetweenRetries) {
+ this.deviceDelayBetweenRetries = device.delayBetweenRetries * 1000;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Device Delay Between Retries: ${this.deviceDelayBetweenRetries}`);
+ } else {
+ this.deviceDelayBetweenRetries = 3000; // Delay between retries in milliseconds
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Delay Between Retries Not Set,`
+ + ` Using: ${this.deviceDelayBetweenRetries}`);
+ }
+ }
+
+ async retryBLE({ max, fn }: { max: number; fn: { (): any; (): Promise } }): Promise {
+ return fn().catch(async (e: any) => {
+ if (max === 0) {
+ throw e;
+ }
+ this.infoLog(e);
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Retrying`);
+ await sleep(1000);
+ return this.retryBLE({ max: max - 1, fn });
+ });
+ }
+
+ async maxRetryBLE(): Promise {
+ if (this.device.maxRetry) {
+ return this.device.maxRetry;
+ } else {
+ return 5;
+ }
+ }
+
+ async scan(accessory: PlatformAccessory, device: device & devicesConfig): Promise {
+ if (device.scanDuration) {
+ if (this.deviceUpdateRate > device.scanDuration) {
+ this.scanDuration = this.deviceUpdateRate;
+ if (this.BLE) {
+ this.warnLog(
+ `${this.device.deviceType}: `
+ + `${accessory.displayName} scanDuration is less than updateRate, overriding scanDuration with updateRate`);
+ }
+ } else {
+ this.scanDuration = accessory.context.scanDuration = device.scanDuration;
+ }
+ if (this.BLE) {
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
+ }
+ } else {
+ if (this.deviceUpdateRate > 1) {
+ this.scanDuration = this.deviceUpdateRate;
+ } else {
+ this.scanDuration = accessory.context.scanDuration = 1;
+ }
+ if (this.BLE) {
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
+ }
+ }
+ }
+
+ async getDeviceConfigSettings(accessory: PlatformAccessory, device: device & devicesConfig): Promise {
+ const deviceConfig = {};
+ if (device.logging !== 'standard') {
+ deviceConfig['logging'] = device.logging;
+ }
+ if (device.refreshRate !== 0) {
+ deviceConfig['refreshRate'] = device.refreshRate;
+ }
+ if (device.updateRate !== 0) {
+ deviceConfig['updateRate'] = device.updateRate;
+ }
+ if (device.scanDuration !== 0) {
+ deviceConfig['scanDuration'] = device.scanDuration;
+ }
+ if (device.offline === true) {
+ deviceConfig['offline'] = device.offline;
+ }
+ if (device.maxRetry !== 0) {
+ deviceConfig['maxRetry'] = device.maxRetry;
+ }
+ if (device.webhook === true) {
+ deviceConfig['webhook'] = device.webhook;
+ }
+ if (device.connectionType !== '') {
+ deviceConfig['connectionType'] = device.connectionType;
+ }
+ if (device.external === true) {
+ deviceConfig['external'] = device.external;
+ }
+ if (device.mqttURL !== '') {
+ deviceConfig['mqttURL'] = device.mqttURL;
+ }
+ if (device.maxRetries !== 0) {
+ deviceConfig['maxRetries'] = device.maxRetries;
+ }
+ if (device.delayBetweenRetries !== 0) {
+ deviceConfig['delayBetweenRetries'] = device.delayBetweenRetries;
+ }
+ let botConfig = {};
+ if (device.bot) {
+ botConfig = device.bot;
+ }
+ let lockConfig = {};
+ if (device.lock) {
+ lockConfig = device.lock;
+ }
+ let ceilinglightConfig = {};
+ if (device.ceilinglight) {
+ ceilinglightConfig = device.ceilinglight;
+ }
+ let colorbulbConfig = {};
+ if (device.colorbulb) {
+ colorbulbConfig = device.colorbulb;
+ }
+ let contactConfig = {};
+ if (device.contact) {
+ contactConfig = device.contact;
+ }
+ let motionConfig = {};
+ if (device.motion) {
+ motionConfig = device.motion;
+ }
+ let curtainConfig = {};
+ if (device.curtain) {
+ curtainConfig = device.curtain;
+ }
+ let hubConfig = {};
+ if (device.hub) {
+ hubConfig = device.hub;
+ }
+ let waterdetectorConfig = {};
+ if (device.waterdetector) {
+ waterdetectorConfig = device.waterdetector;
+ }
+ let humidifierConfig = {};
+ if (device.humidifier) {
+ humidifierConfig = device.humidifier;
+ }
+ let meterConfig = {};
+ if (device.meter) {
+ meterConfig = device.meter;
+ }
+ let iosensorConfig = {};
+ if (device.iosensor) {
+ iosensorConfig = device.iosensor;
+ }
+ let striplightConfig = {};
+ if (device.striplight) {
+ striplightConfig = device.striplight;
+ }
+ let plugConfig = {};
+ if (device.plug) {
+ plugConfig = device.plug;
+ }
+ let blindTiltConfig = {};
+ if (device.blindTilt) {
+ if (device.blindTilt?.mode === undefined) {
+ blindTiltConfig['mode'] = BlindTiltMappingMode.OnlyUp;
+ }
+ blindTiltConfig = device.blindTilt;
+ }
+ const config = Object.assign({}, deviceConfig, botConfig, curtainConfig, waterdetectorConfig, striplightConfig, plugConfig, iosensorConfig,
+ meterConfig, humidifierConfig, hubConfig, lockConfig, ceilinglightConfig, colorbulbConfig, contactConfig, motionConfig, blindTiltConfig);
+ if (Object.entries(config).length !== 0) {
+ this.debugSuccessLog(`${this.device.deviceType}: ${accessory.displayName} Config: ${JSON.stringify(config)}`);
+ }
+ }
+
+ /*
+ * Publish MQTT message for topics of
+ * 'homebridge-switchbot/${this.device.deviceType}/xx:xx:xx:xx:xx:xx'
+ */
+ mqttPublish(message: string, topic?: string) {
+ const mac = this.device.deviceId
+ ?.toLowerCase()
+ .match(/[\s\S]{1,2}/g)
+ ?.join(':');
+ const options = this.device.mqttPubOptions || {};
+ let mqttTopic: string;
+ let mqttMessageTopic: string;
+ if (topic) {
+ mqttTopic = `/${topic}`;
+ mqttMessageTopic = `${topic}/`;
+ } else {
+ mqttTopic = '';
+ mqttMessageTopic = '';
+ }
+ this.mqttClient?.publish(`homebridge-switchbot/${this.device.deviceType}/${mac}${mqttTopic}`, `${message}`, options);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` MQTT message: ${mqttMessageTopic}${message} options:${JSON.stringify(options)}`);
+ }
+
+ /*
+ * Setup MQTT hadler if URL is specified.
+ */
+ async setupMqtt(accessory: PlatformAccessory, device: device & devicesConfig): Promise {
+ if (device.mqttURL) {
+ try {
+ const { connectAsync } = asyncmqtt;
+ this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`);
+ this.mqttClient.on('error', (e: Error) => {
+ this.errorLog(`${this.device.deviceType}: ${accessory.displayName} Failed to publish MQTT messages. ${e}`);
+ });
+ } catch (e) {
+ this.mqttClient = null;
+ this.errorLog(`${this.device.deviceType}: ${accessory.displayName} Failed to establish MQTT connection. ${e}`);
+ }
+ }
+ }
+
+ /*
+ * Setup EVE history graph feature if enabled.
+ */
+ async setupHistoryService(accessory: PlatformAccessory, device: device & devicesConfig): Promise {
+ const mac = this.device
+ .deviceId!.match(/.{1,2}/g)!
+ .join(':')
+ .toLowerCase();
+ this.historyService = device.history
+ ? new this.platform.fakegatoAPI('room', accessory, {
+ log: this.platform.log,
+ storage: 'fs',
+ filename: `${hostname().split('.')[0]}_${mac}_persist.json`,
+ })
+ : null;
+ }
+
+ async getCustomBLEAddress(switchbot: any) {
+ if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} customBLEaddress: ${this.device.customBLEaddress}`);
+ (async () => {
+ // Start to monitor advertisement packets
+ await switchbot.startScan({ model: this.device.bleModel });
+ // Set an event handler
+ switchbot.onadvertisement = (ad: any) => {
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
+ };
+ await sleep(10000);
+ // Stop to monitor
+ switchbot.stopScan();
+ })();
+ }
+ }
+
+ async getDeviceContext(accessory: PlatformAccessory, device: device & devicesConfig): Promise {
+ // Set the accessory context
+ switch (device.deviceType) {
+ case 'Humidifier':
+ device.model = SwitchBotModel.Humidifier;
+ device.bleModel = SwitchBotBLEModel.Humidifier;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Hub Mini':
+ device.model = SwitchBotModel.HubMini;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Hub Plus':
+ device.model = SwitchBotModel.HubPlus;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Hub 2':
+ device.model = SwitchBotModel.Hub2;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Bot':
+ device.model = SwitchBotModel.Bot;
+ device.bleModel = SwitchBotBLEModel.Bot;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Meter':
+ device.model = SwitchBotModel.Meter;
+ device.bleModel = SwitchBotBLEModel.Meter;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'MeterPlus':
+ device.model = SwitchBotModel.MeterPlusUS;
+ device.bleModel = SwitchBotBLEModel.MeterPlus;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Meter Plus (JP)':
+ device.model = SwitchBotModel.MeterPlusJP;
+ device.bleModel = SwitchBotBLEModel.MeterPlus;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'WoIOSensor':
+ device.model = SwitchBotModel.OutdoorMeter;
+ device.bleModel = SwitchBotBLEModel.OutdoorMeter;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Water Detector':
+ device.model = SwitchBotModel.WaterDetector;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Motion Sensor':
+ device.model = SwitchBotModel.MotionSensor;
+ device.bleModel = SwitchBotBLEModel.MotionSensor;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Contact Sensor':
+ device.model = SwitchBotModel.ContactSensor;
+ device.bleModel = SwitchBotBLEModel.ContactSensor;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Curtain':
+ device.model = SwitchBotModel.Curtain;
+ device.bleModel = SwitchBotBLEModel.Curtain;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Curtain3':
+ device.model = SwitchBotModel.Curtain3;
+ device.bleModel = SwitchBotBLEModel.Curtain3;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Blind Tilt':
+ device.model = SwitchBotModel.BlindTilt;
+ device.bleModel = SwitchBotBLEModel.BlindTilt;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Plug':
+ device.model = SwitchBotModel.Plug;
+ device.bleModel = SwitchBotBLEModel.PlugMiniUS;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Plug Mini (US)':
+ device.model = SwitchBotModel.PlugMiniUS;
+ device.bleModel = SwitchBotBLEModel.PlugMiniUS;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Plug Mini (JP)':
+ device.model = SwitchBotModel.PlugMiniJP;
+ device.bleModel = SwitchBotBLEModel.PlugMiniJP;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Smart Lock':
+ device.model = SwitchBotModel.Lock;
+ device.bleModel = SwitchBotBLEModel.Lock;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Smart Lock Pro':
+ device.model = SwitchBotModel.LockPro;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Color Bulb':
+ device.model = SwitchBotModel.ColorBulb;
+ device.bleModel = SwitchBotBLEModel.ColorBulb;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'K10+':
+ device.model = SwitchBotModel.K10;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'WoSweeper':
+ device.model = SwitchBotModel.WoSweeper;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'WoSweeperMini':
+ device.model = SwitchBotModel.WoSweeperMini;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Robot Vacuum Cleaner S1':
+ device.model = SwitchBotModel.RobotVacuumCleanerS1;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Robot Vacuum Cleaner S1 Plus':
+ device.model = SwitchBotModel.RobotVacuumCleanerS1Plus;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Robot Vacuum Cleaner S10':
+ device.model = SwitchBotModel.RobotVacuumCleanerS10;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Ceiling Light':
+ device.model = SwitchBotModel.CeilingLight;
+ device.bleModel = SwitchBotBLEModel.CeilingLight;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Ceiling Light Pro':
+ device.model = SwitchBotModel.CeilingLightPro;
+ device.bleModel = SwitchBotBLEModel.CeilingLightPro;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Strip Light':
+ device.model = SwitchBotModel.StripLight;
+ device.bleModel = SwitchBotBLEModel.StripLight;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Indoor Cam':
+ device.model = SwitchBotModel.IndoorCam;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Remote':
+ device.model = SwitchBotModel.Remote;
+ device.bleModel = SwitchBotBLEModel.Remote;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'remote with screen+':
+ device.model = SwitchBotModel.UniversalRemote;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ case 'Battery Circulator Fan':
+ device.model = SwitchBotModel.BatteryCirculatorFan;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ break;
+ default:
+ device.model = SwitchBotModel.Unknown;
+ device.bleModel = SwitchBotBLEModel.Unknown;
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Model: ${device.model}, BLE Model: ${device.bleModel}`);
+ }
+ accessory.context.model = device.model;
+
+ // Firmware Version
+ let deviceFirmwareVersion: string;
+ if (device.firmware) {
+ deviceFirmwareVersion = device.firmware;
+ this.debugSuccessLog(`${device.deviceType}: ${accessory.displayName} 1 FirmwareRevision: ${device.firmware}`);
+ } else if (device.version) {
+ deviceFirmwareVersion = device.version;
+ this.debugSuccessLog(`${device.deviceType}: ${accessory.displayName} 2 FirmwareRevision: ${device.version}`);
+ } else if (accessory.context.deviceVersion) {
+ deviceFirmwareVersion = accessory.context.deviceVersion;
+ this.debugSuccessLog(`${device.deviceType}: ${accessory.displayName} 3 FirmwareRevision: ${accessory.context.deviceVersion}`);
+ } else {
+ deviceFirmwareVersion = this.platform.version ?? '0.0.0';
+ if (this.platform.version) {
+ this.debugSuccessLog(`${device.deviceType}: ${accessory.displayName} 4 FirmwareRevision: ${this.platform.version}`);
+ } else {
+ this.debugSuccessLog(`${device.deviceType}: ${accessory.displayName} 5 FirmwareRevision: ${deviceFirmwareVersion}`);
+ }
+ }
+ const version = deviceFirmwareVersion.toString();
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
+ let deviceVersion: string;
+ if (version?.includes('.') === false) {
+ const replace = version?.replace(/^V|-.*$/g, '');
+ const match = replace?.match(/.{1,1}/g);
+ const validVersion = match?.join('.');
+ deviceVersion = validVersion ?? '0.0.0';
+ } else {
+ deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
+ }
+ accessory
+ .getService(this.hap.Service.AccessoryInformation)!
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
+ .setCharacteristic(this.hap.Characteristic.SoftwareRevision, deviceVersion)
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
+ .updateValue(deviceVersion);
+ accessory.context.deviceVersion = deviceVersion;
+ this.debugSuccessLog(`${device.deviceType}: ${accessory.displayName} deviceVersion: ${accessory.context.deviceVersion}`);
+ }
+
+ async statusCode(statusCode: number): Promise {
+ if (statusCode === 171) {
+ const previousStatusCode = statusCode;
+ if (this.device.hubDeviceId === this.device.deviceId) {
+ statusCode = 161;
+ this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${previousStatusCode} is now statusCode: `
+ + `${statusCode}, because the hubDeviceId: ${this.device.hubDeviceId} is set to the same as the deviceId: `
+ + `${this.device.deviceId}, meaning the device is it's own hub.`);
+ }
+ if (this.device.hubDeviceId === '000000000000') {
+ statusCode = 161;
+ this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${previousStatusCode} is now statusCode: `
+ + `${statusCode}, because the hubDeviceId: ${this.device.hubDeviceId} is set to the same as the deviceId: `
+ + `${this.device.deviceId}, meaning the device is it's own hub.`);
+ }
+ }
+ switch (statusCode) {
+ case 151:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
+ break;
+ case 152:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
+ break;
+ case 160:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
+ break;
+ case 161:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
+ break;
+ case 171:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. `
+ + `Hub: ${this.device.hubDeviceId}`);
+ break;
+ case 190:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with`
+ + ` server, Or command format is invalid, statusCode: ${statusCode}`);
+ break;
+ case 100:
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
+ break;
+ case 200:
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
+ break;
+ case 400:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. `
+ + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`);
+ break;
+ case 401:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, `
+ + `but the request has not been authenticated, statusCode: ${statusCode}`);
+ break;
+ case 403:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not `
+ + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`);
+ break;
+ case 404:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, `
+ + `statusCode: ${statusCode}`);
+ break;
+ case 406:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via `
+ + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`);
+ break;
+ case 415:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType `
+ + `header that is not supported by the server, statusCode: ${statusCode}`);
+ break;
+ case 422:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, The client has made a valid request, `
+ + `but the server cannot process it. This is often used for APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
+ break;
+ case 429:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of `
+ + `requests allowed for a given time window, statusCode: ${statusCode}`);
+ break;
+ case 500:
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings `
+ + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`);
+ break;
+ default:
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: `
+ + `${statusCode}, Submit Bugs Here: https://tinyurl.com/SwitchBotBug`);
+ }
+ }
+
+ /**
+ * Logging for Device
+ */
+ infoLog(...log: any[]): void {
+ if (this.enablingDeviceLogging()) {
+ this.log.info(String(...log));
+ }
+ }
+
+ successLog(...log: any[]): void {
+ if (this.enablingDeviceLogging()) {
+ this.log.success(String(...log));
+ }
+ }
+
+ debugSuccessLog(...log: any[]): void {
+ if (this.enablingDeviceLogging()) {
+ if (this.deviceLogging?.includes('debug')) {
+ this.log.success('[DEBUG]', String(...log));
+ }
+ }
+ }
+
+ warnLog(...log: any[]): void {
+ if (this.enablingDeviceLogging()) {
+ this.log.warn(String(...log));
+ }
+ }
+
+ debugWarnLog(...log: any[]): void {
+ if (this.enablingDeviceLogging()) {
+ if (this.deviceLogging?.includes('debug')) {
+ this.log.warn('[DEBUG]', String(...log));
+ }
+ }
+ }
+
+ errorLog(...log: any[]): void {
+ if (this.enablingDeviceLogging()) {
+ this.log.error(String(...log));
+ }
+ }
+
+ debugErrorLog(...log: any[]): void {
+ if (this.enablingDeviceLogging()) {
+ if (this.deviceLogging?.includes('debug')) {
+ this.log.error('[DEBUG]', String(...log));
+ }
+ }
+ }
+
+ debugLog(...log: any[]): void {
+ if (this.enablingDeviceLogging()) {
+ if (this.deviceLogging === 'debug') {
+ this.log.info('[DEBUG]', String(...log));
+ } else {
+ this.log.debug(String(...log));
+ }
+ }
+ }
+
+ enablingDeviceLogging(): boolean {
+ return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard';
+ }
+}
\ No newline at end of file
diff --git a/src/device/fan.ts b/src/device/fan.ts
new file mode 100644
index 00000000..1b46e237
--- /dev/null
+++ b/src/device/fan.ts
@@ -0,0 +1,749 @@
+/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+ *
+ * plug.ts: @switchbot/homebridge-switchbot.
+ */
+import { request } from 'undici';
+import { deviceBase } from './device.js';
+import { Devices } from '../settings.js';
+import { Subject, debounceTime, interval, skipWhile, take, tap } from 'rxjs';
+
+import type { SwitchBotPlatform } from '../platform.js';
+import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge';
+import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js';
+
+export class Fan extends deviceBase {
+ // Services
+ private Fan: {
+ Name: CharacteristicValue;
+ Service: Service;
+ Active: CharacteristicValue;
+ SwingMode: CharacteristicValue;
+ RotationSpeed: CharacteristicValue;
+ };
+
+ private Battery: {
+ Name: CharacteristicValue;
+ Service: Service;
+ BatteryLevel: CharacteristicValue;
+ StatusLowBattery: CharacteristicValue;
+ ChargingState: CharacteristicValue;
+ };
+
+ private LightBulb: {
+ Name: CharacteristicValue;
+ Service: Service;
+ On: CharacteristicValue;
+ Brightness: CharacteristicValue;
+ };
+
+ // Updates
+ fanUpdateInProgress!: boolean;
+ doFanUpdate!: Subject;
+
+ constructor(
+ readonly platform: SwitchBotPlatform,
+ accessory: PlatformAccessory,
+ device: device & devicesConfig,
+ ) {
+ super(platform, accessory, device);
+ // this is subject we use to track when we need to POST changes to the SwitchBot API
+ this.doFanUpdate = new Subject();
+ this.fanUpdateInProgress = false;
+
+ // Initialize Fan Service
+ accessory.context.Fan = accessory.context.Fan ?? {};
+ this.Fan = {
+ Name: accessory.context.Fan.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.Fanv2) ?? accessory.addService(this.hap.Service.Fanv2) as Service,
+ Active: accessory.context.Active ?? this.hap.Characteristic.Active.INACTIVE,
+ SwingMode: accessory.context.SwingMode ?? this.hap.Characteristic.SwingMode.SWING_DISABLED,
+ RotationSpeed: accessory.context.RotationSpeed ?? 0,
+ };
+ accessory.context.Fan = this.Fan as object;
+
+ // Initialize Fan Service
+ this.Fan.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.Fan.Name)
+ .getCharacteristic(this.hap.Characteristic.Active)
+ .onGet(() => {
+ return this.Fan.Active;
+ })
+ .onSet(this.ActiveSet.bind(this));
+
+ // Initialize Fan RotationSpeed Characteristic
+ this.Fan.Service
+ .getCharacteristic(this.hap.Characteristic.RotationSpeed)
+ .onGet(() => {
+ return this.Fan.RotationSpeed;
+ })
+ .onSet(this.RotationSpeedSet.bind(this));
+
+ // Initialize Fan SwingMode Characteristic
+ this.Fan.Service.getCharacteristic(this.hap.Characteristic.SwingMode)
+ .onGet(() => {
+ return this.Fan.SwingMode;
+ })
+ .onSet(this.SwingModeSet.bind(this));
+
+ // Initialize Battery Service
+ accessory.context.Battery = accessory.context.Battery ?? {};
+ this.Battery = {
+ Name: accessory.context.Battery.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service,
+ BatteryLevel: accessory.context.BatteryLevel ?? 100,
+ StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL,
+ ChargingState: accessory.context.ChargingState ?? this.hap.Characteristic.ChargingState.NOT_CHARGING,
+ };
+ accessory.context.Battery = this.Battery as object;
+
+ // Initialize Battery Service
+ this.Battery.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name)
+ .getCharacteristic(this.hap.Characteristic.BatteryLevel)
+ .onGet(() => {
+ return this.Battery.BatteryLevel;
+ });
+
+ // Initialize Battery ChargingState Characteristic
+ this.Battery.Service
+ .getCharacteristic(this.hap.Characteristic.ChargingState)
+ .onGet(() => {
+ return this.Battery.ChargingState;
+ });
+
+ // Initialize Battery StatusLowBattery Characteristic
+ this.Battery.Service
+ .getCharacteristic(this.hap.Characteristic.StatusLowBattery)
+ .onGet(() => {
+ return this.Battery.StatusLowBattery;
+ });
+
+ // Initialize LightBulb Service
+ accessory.context.LightBulb = accessory.context.LightBulb ?? {};
+ this.LightBulb = {
+ Name: accessory.context.LightBulb.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.Lightbulb) ?? accessory.addService(this.hap.Service.Lightbulb) as Service,
+ On: accessory.context.On ?? false,
+ Brightness: accessory.context.Brightness ?? 0,
+ };
+ accessory.context.LightBulb = this.LightBulb as object;
+
+ // Initialize LightBulb Characteristics
+ this.LightBulb.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.LightBulb.Name)
+ .getCharacteristic(this.hap.Characteristic.On)
+ .onGet(() => {
+ return this.LightBulb.On;
+ })
+ .onSet(this.OnSet.bind(this));
+
+ // Initialize LightBulb Brightness Characteristic
+ this.LightBulb.Service
+ .getCharacteristic(this.hap.Characteristic.Brightness)
+ .onGet(() => {
+ return this.LightBulb.Brightness;
+ })
+ .onSet(this.BrightnessSet.bind(this));
+
+ // Retrieve initial values and updateHomekit
+ this.refreshStatus();
+
+ // Update Homekit
+ this.updateHomeKitCharacteristics();
+
+ // Start an update interval
+ interval(this.deviceRefreshRate * 1000)
+ .pipe(skipWhile(() => this.fanUpdateInProgress))
+ .subscribe(async () => {
+ await this.refreshStatus();
+ });
+
+ //regisiter webhook event handler
+ this.registerWebhook(accessory, device);
+
+ // Watch for Plug change events
+ // We put in a debounce of 100ms so we don't make duplicate calls
+ this.doFanUpdate
+ .pipe(
+ tap(() => {
+ this.fanUpdateInProgress = true;
+ }),
+ debounceTime(this.devicePushRate * 1000),
+ )
+ .subscribe(async () => {
+ try {
+ await this.pushChanges();
+ } catch (e: any) {
+ this.apiError(e);
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} failed pushChanges with ${device.connectionType} Connection,`
+ + ` Error Message: ${JSON.stringify(e.message)}`);
+ }
+ this.fanUpdateInProgress = false;
+ });
+ }
+
+ async BLEparseStatus(serviceData: serviceData): Promise {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
+
+ // State
+ switch (serviceData.state) {
+ case 'on':
+ this.Fan.Active = true;
+ break;
+ default:
+ this.Fan.Active = false;
+ }
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Fan.Active}`);
+ }
+
+ async openAPIparseStatus(deviceStatus: deviceStatus) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
+
+ // Active
+ this.Fan.Active = deviceStatus.body.power === 'on' ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.Fan.Active}`);
+
+ // SwingMode
+ this.Fan.SwingMode = deviceStatus.body.oscillation === 'on' ?
+ this.hap.Characteristic.SwingMode.SWING_ENABLED : this.hap.Characteristic.SwingMode.SWING_DISABLED;
+
+ // RotationSpeed
+ this.Fan.RotationSpeed = deviceStatus.body.fanSpeed;
+
+ // ChargingState
+ this.Battery.ChargingState = deviceStatus.body.chargingStatus === 'charging' ?
+ this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING;
+
+ // BatteryLevel
+ this.Battery.BatteryLevel = Number(deviceStatus.body.battery);
+ if (this.Battery.BatteryLevel < 10) {
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
+ } else {
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
+ }
+ if (Number.isNaN(this.Battery.BatteryLevel)) {
+ this.Battery.BatteryLevel = 100;
+ }
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
+
+ // Firmware Version
+ const version = deviceStatus.body.version?.toString();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
+ if (deviceStatus.body.version) {
+ const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
+ this.accessory
+ .getService(this.hap.Service.AccessoryInformation)!
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
+ .updateValue(deviceVersion);
+ this.accessory.context.deviceVersion = deviceVersion;
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
+ }
+ }
+
+
+
+ /**
+ * Asks the SwitchBot API for the latest device information
+ */
+ async refreshStatus(): Promise {
+ if (!this.device.enableCloudService && this.OpenAPI) {
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} refreshStatus enableCloudService: ${this.device.enableCloudService}`);
+ } else if (this.BLE) {
+ await this.BLERefreshStatus();
+ } else if (this.OpenAPI && this.platform.config.credentials?.token) {
+ await this.openAPIRefreshStatus();
+ } else {
+ await this.offlineOff();
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
+ }
+ }
+
+ async BLERefreshStatus(): Promise {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLERefreshStatus`);
+ const switchbot = await this.platform.connectBLE();
+ // Convert to BLE Address
+ this.device.bleMac = this.device
+ .deviceId!.match(/.{1,2}/g)!
+ .join(':')
+ .toLowerCase();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
+ this.getCustomBLEAddress(switchbot);
+ // Start to monitor advertisement packets
+ (async () => {
+ // Start to monitor advertisement packets
+ await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac });
+ // Set an event handler
+ switchbot.onadvertisement = (ad: any) => {
+ if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
+ } else {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
+ }
+ };
+ // Wait 10 seconds
+ await switchbot.wait(this.scanDuration * 1000);
+ // Stop to monitor
+ await switchbot.stopScan();
+ // Update HomeKit
+ await this.BLEparseStatus(switchbot.onadvertisement.serviceData);
+ await this.updateHomeKitCharacteristics();
+ })();
+ if (switchbot === undefined) {
+ await this.BLERefreshConnection(switchbot);
+ }
+ }
+
+ async openAPIRefreshStatus(): Promise {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
+ try {
+ const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries,
+ `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() });
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
+ const deviceStatus: any = await body.json();
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
+ if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.openAPIparseStatus(deviceStatus);
+ this.updateHomeKitCharacteristics();
+ } else {
+ this.statusCode(statusCode);
+ this.statusCode(deviceStatus.statusCode);
+ }
+ } catch (e: any) {
+ this.apiError(e);
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
+ }
+ }
+
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
+ if (device.webhook) {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`);
+ this.platform.webhookEventHandler[device.deviceId] = async (context) => {
+ try {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
+ const { version, battery, powerState, oscillation, chargingStatus, fanSpeed } = context;
+ const { Active, SwingMode, RotationSpeed } = this.Fan;
+ const { BatteryLevel, ChargingState } = this.Battery;
+ const { FirmwareRevision } = accessory.context;
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} (version, battery, powerState, oscillation, chargingStatus, fanSpeed) = `
+ + `Webhook:(${version}, ${battery}, ${powerState}, ${oscillation}, ${chargingStatus}, ${fanSpeed}), `
+ + `current:(${FirmwareRevision}, ${BatteryLevel}, ${Active}, ${SwingMode}, ${ChargingState}, ${RotationSpeed})`);
+
+ // Active
+ this.Fan.Active = powerState === 'ON' ? this.hap.Characteristic.Active.ACTIVE : this.hap.Characteristic.Active.INACTIVE;
+
+ // SwingMode
+ this.Fan.SwingMode = oscillation === 'on' ?
+ this.hap.Characteristic.SwingMode.SWING_ENABLED : this.hap.Characteristic.SwingMode.SWING_DISABLED;
+
+ // RotationSpeed
+ this.Fan.RotationSpeed = fanSpeed;
+
+ // ChargingState
+ this.Battery.ChargingState = chargingStatus === 'charging' ?
+ this.hap.Characteristic.ChargingState.CHARGING : this.hap.Characteristic.ChargingState.NOT_CHARGING;
+
+ // BatteryLevel
+ this.Battery.BatteryLevel = battery;
+
+ // Firmware Version
+ if (version) {
+ accessory.context.version = version;
+ accessory
+ .getService(this.hap.Service.AccessoryInformation)!
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.version)
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
+ .updateValue(accessory.context.version);
+ }
+ this.updateHomeKitCharacteristics();
+ } catch (e: any) {
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
+ }
+ };
+ } else {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`);
+ }
+ }
+
+ /**
+ * Pushes the requested changes to the SwitchBot API
+ * commandType command parameter Description
+ * "command" "turnOff" "default" = set to OFF state
+ * "command" "turnOn" "default" = set to ON state
+ * "command" "setNightLightMode" "off, 1, or 2" = off, turn off nightlight, (1, bright) (2, dim)
+ * "command" "setWindMode" "direct, natural, sleep, or baby" = Set fan mode
+ * "command" "setWindSpeed" "{1-100} e.g. 10" = Set fan speed 1~100
+ */
+
+ async pushChanges(): Promise {
+ if (!this.device.enableCloudService && this.OpenAPI) {
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} pushChanges enableCloudService: ${this.device.enableCloudService}`);
+ } else if (this.BLE) {
+ await this.BLEpushChanges();
+ } else if (this.OpenAPI && this.platform.config.credentials?.token) {
+ await this.openAPIpushChanges();
+ } else {
+ await this.offlineOff();
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, pushChanges will not happen.`);
+ }
+ // Refresh the status from the API
+ interval(15000)
+ .pipe(skipWhile(() => this.fanUpdateInProgress))
+ .pipe(take(1))
+ .subscribe(async () => {
+ await this.refreshStatus();
+ });
+ }
+
+ async BLEpushChanges(): Promise {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`);
+ if (this.Fan.Active !== this.accessory.context.Active) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEpushChanges`
+ + ` On: ${this.Fan.Active} OnCached: ${this.accessory.context.Active}`);
+ const switchbot = await this.platform.connectBLE();
+ // Convert to BLE Address
+ this.device.bleMac = this.device
+ .deviceId!.match(/.{1,2}/g)!
+ .join(':')
+ .toLowerCase();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE Address: ${this.device.bleMac}`);
+ switchbot
+ .discover({
+ model: this.device.bleModel,
+ id: this.device.bleMac,
+ })
+ .then(async (device_list: any) => {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} On: ${this.Fan.Active}`);
+ return await this.retryBLE({
+ max: await this.maxRetryBLE(),
+ fn: async () => {
+ if (this.Fan.Active) {
+ return await device_list[0].turnOn({ id: this.device.bleMac });
+ } else {
+ return await device_list[0].turnOff({ id: this.device.bleMac });
+ }
+ },
+ });
+ })
+ .then(() => {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `Active: ${this.Fan.Active} sent over BLE, sent successfully`);
+ this.Fan.Active = false;
+ })
+ .catch(async (e: any) => {
+ this.apiError(e);
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
+ await this.BLEPushConnection();
+ });
+ } else {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No BLEpushChanges,`
+ + ` Active: ${this.Fan.Active}, ActiveCached: ${this.accessory.context.Active}`);
+ }
+ }
+
+ async openAPIpushChanges() {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`);
+ if (this.Fan.Active !== this.accessory.context.Active) {
+ let command = '';
+ if (this.Fan.Active) {
+ command = 'turnOn';
+ } else {
+ command = 'turnOff';
+ }
+ const bodyChange = JSON.stringify({
+ command: `${command}`,
+ parameter: 'default',
+ commandType: 'command',
+ });
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`);
+ try {
+ const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, {
+ body: bodyChange,
+ method: 'POST',
+ headers: this.platform.generateHeaders(),
+ });
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
+ const deviceStatus: any = await body.json();
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`);
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
+ if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
+ this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
+ } else {
+ this.statusCode(statusCode);
+ this.statusCode(deviceStatus.statusCode);
+ }
+ } catch (e: any) {
+ this.apiError(e);
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
+ }
+ } else {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No openAPIpushChanges.`
+ + `On: ${this.Fan.Active}, ActiveCached: ${this.accessory.context.Active}`);
+ }
+ // Push RotationSpeed Update
+ if (this.Fan.Active) {
+ await this.pushRotationSpeedChanges();
+ }
+ // Push SwingMode Update
+ if (this.Fan.Active) {
+ await this.pushSwingModeChanges();
+ }
+ }
+
+ async pushRotationSpeedChanges(): Promise {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushRotationSpeedChanges`);
+ if (this.Fan.SwingMode !== this.accessory.context.SwingMode) {
+ const bodyChange = JSON.stringify({
+ command: 'setWindSpeed',
+ parameter: `${this.Fan.RotationSpeed}`,
+ commandType: 'command',
+ });
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`);
+ try {
+ const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, {
+ body: bodyChange,
+ method: 'POST',
+ headers: this.platform.generateHeaders(),
+ });
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
+ const deviceStatus: any = await body.json();
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`);
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
+ if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
+ this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
+ } else {
+ this.statusCode(statusCode);
+ this.statusCode(deviceStatus.statusCode);
+ }
+ } catch (e: any) {
+ this.apiError(e);
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushRotationSpeedChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
+ }
+ } else {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, pushRotationSpeedChanges: ${this.Fan.RotationSpeed}, `
+ + `pushRotationSpeedChangesCached: ${this.accessory.context.RotationSpeed}`);
+ }
+ }
+
+ async pushSwingModeChanges(): Promise {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushSwingModeChanges`);
+ if (this.Fan.SwingMode !== this.accessory.context.SwingMode) {
+ let parameter = '';
+ if (this.Fan.SwingMode === this.hap.Characteristic.SwingMode.SWING_ENABLED) {
+ parameter = 'on';
+ } else {
+ parameter = 'off';
+ }
+ const bodyChange = JSON.stringify({
+ command: 'setOscillation',
+ parameter: `${parameter}`,
+ commandType: 'command',
+ });
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`);
+ try {
+ const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/commands`, {
+ body: bodyChange,
+ method: 'POST',
+ headers: this.platform.generateHeaders(),
+ });
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
+ const deviceStatus: any = await body.json();
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus body: ${JSON.stringify(deviceStatus.body)}`);
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
+ if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
+ this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
+ } else {
+ this.statusCode(statusCode);
+ this.statusCode(deviceStatus.statusCode);
+ }
+ } catch (e: any) {
+ this.apiError(e);
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushSwingModeChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
+ }
+ } else {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, SwingMode: ${this.Fan.SwingMode}, `
+ + `SwingModeCached: ${this.accessory.context.SwingMode}`);
+ }
+ }
+
+ /**
+ * Handle requests to set the value of the "On" characteristic
+ */
+ async ActiveSet(value: CharacteristicValue): Promise {
+ if (this.Fan.Active === this.accessory.context.Active) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Active: ${value}`);
+ } else {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Active: ${value}`);
+ }
+
+ this.Fan.Active = value;
+ this.doFanUpdate.next();
+ }
+
+ /**
+ * Handle requests to set the value of the "On" characteristic
+ */
+ async RotationSpeedSet(value: CharacteristicValue): Promise {
+ if (this.Fan.RotationSpeed === this.accessory.context.RotationSpeed) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set RotationSpeed: ${value}`);
+ } else {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set RotationSpeed: ${value}`);
+ }
+
+ this.Fan.RotationSpeed = value;
+ this.doFanUpdate.next();
+ }
+
+ /**
+ * Handle requests to set the value of the "On" characteristic
+ */
+ async SwingModeSet(value: CharacteristicValue): Promise {
+ if (this.Fan.SwingMode === this.accessory.context.SwingMode) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set SwingMode: ${value}`);
+ } else {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set SwingMode: ${value}`);
+ }
+
+ this.Fan.SwingMode = value;
+ this.doFanUpdate.next();
+ }
+
+ /**
+ * Handle requests to set the value of the "On" characteristic
+ */
+ async OnSet(value: CharacteristicValue): Promise {
+ if (this.LightBulb.On === this.accessory.context.On) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set On: ${value}`);
+ } else {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set On: ${value}`);
+ }
+
+ this.LightBulb.On = value;
+ this.doFanUpdate.next();
+ }
+
+ /**
+ * Handle requests to set the value of the "Brightness" characteristic
+ */
+ async BrightnessSet(value: CharacteristicValue): Promise {
+ if (this.LightBulb.Brightness === this.accessory.context.Brightness) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Brightness: ${value}`);
+ } else if (this.LightBulb.On) {
+ this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`);
+ } else {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Brightness: ${value}`);
+ }
+
+ this.LightBulb.Brightness = value;
+ this.doFanUpdate.next();
+ }
+
+ async updateHomeKitCharacteristics(): Promise {
+ // Active
+ if (this.Fan.Active === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.Fan.Active}`);
+ } else {
+ this.accessory.context.Active = this.Fan.Active;
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.Active, this.Fan.Active);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Fan.Active}`);
+ }
+ // RotationSpeed
+ if (this.Fan.RotationSpeed === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} RotationSpeed: ${this.Fan.RotationSpeed}`);
+ } else {
+ this.accessory.context.RotationSpeed = this.Fan.RotationSpeed;
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.RotationSpeed, this.Fan.RotationSpeed);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic RotationSpeed: ${this.Fan.RotationSpeed}`);
+ }
+ // SwingMode
+ if (this.Fan.SwingMode === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} SwingMode: ${this.Fan.SwingMode}`);
+ } else {
+ this.accessory.context.SwingMode = this.Fan.SwingMode;
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.SwingMode, this.Fan.SwingMode);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic SwingMode: ${this.Fan.SwingMode}`);
+ }
+ // BateryLevel
+ if (this.Battery.BatteryLevel === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`);
+ } else {
+ this.accessory.context.BatteryLevel = this.Battery.BatteryLevel;
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`);
+ }
+ // ChargingState
+ if (this.Battery.ChargingState === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ChargingState: ${this.Battery.ChargingState}`);
+ } else {
+ this.accessory.context.ChargingState = this.Battery.ChargingState;
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.ChargingState, this.Battery.ChargingState);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic ChargingState: ${this.Battery.ChargingState}`);
+ }
+ // StatusLowBattery
+ if (this.Battery.StatusLowBattery === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`);
+ } else {
+ this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery;
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
+ }
+ }
+
+ async BLEPushConnection() {
+ if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`);
+ await this.openAPIpushChanges();
+ }
+ }
+
+ async BLERefreshConnection(switchbot: any): Promise {
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:`
+ + ` ${JSON.stringify(switchbot)}`);
+ if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Refresh Status`);
+ await this.openAPIRefreshStatus();
+ }
+ }
+
+ async offlineOff(): Promise {
+ if (this.device.offline) {
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE);
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.RotationSpeed, 0);
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.SwingMode, this.hap.Characteristic.SwingMode.SWING_DISABLED);
+ }
+ }
+
+ async apiError(e: any): Promise {
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.Active, e);
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.RotationSpeed, e);
+ this.Fan.Service.updateCharacteristic(this.hap.Characteristic.SwingMode, e);
+ }
+}
diff --git a/src/device/hub.ts b/src/device/hub.ts
index 3ebded2c..94b4bff0 100644
--- a/src/device/hub.ts
+++ b/src/device/hub.ts
@@ -1,106 +1,70 @@
-import asyncmqtt from 'async-mqtt';
-import { CharacteristicValue, PlatformAccessory, Service, Units, API, Logging, HAP } from 'homebridge';
-import { MqttClient } from 'mqtt';
-import { hostname } from 'os';
-import { interval } from 'rxjs';
-import { request } from 'undici';
-import { SwitchBotPlatform } from '../platform.js';
-import { Devices, device, deviceStatus, devicesConfig, SwitchBotPlatformConfig } from '../settings.js';
-
-export class Hub {
- public readonly api: API;
- public readonly log: Logging;
- public readonly config!: SwitchBotPlatformConfig;
- protected readonly hap: HAP;
+/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+ *
+ * hub.ts: @switchbot/homebridge-switchbot.
+ */
+import { Units } from 'homebridge';
+import { deviceBase } from './device.js';
+import { Devices } from '../settings.js';
+import { convertUnits } from '../utils.js';
+import { Subject, interval, skipWhile } from 'rxjs';
+
+import type { SwitchBotPlatform } from '../platform.js';
+import type { device, deviceStatus, devicesConfig } from '../settings.js';
+import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge';
+
+export class Hub extends deviceBase {
// Services
- lightSensorService?: Service;
- humidityService?: Service;
- temperatureService?: Service;
-
- // Characteristic Values
- FirmwareRevision!: CharacteristicValue;
- CurrentTemperature!: CharacteristicValue;
- CurrentRelativeHumidity!: CharacteristicValue;
- CurrentAmbientLightLevel!: CharacteristicValue;
-
- // OpenAPI Status
- OpenAPI_FirmwareRevision: deviceStatus['version'];
- OpenAPI_CurrentTemperature: deviceStatus['temperature'];
- OpenAPI_CurrentRelativeHumidity: deviceStatus['humidity'];
- OpenAPI_CurrentAmbientLightLevel!: deviceStatus['brightness'];
-
- // OpenAPI Others
- spaceBetweenLevels!: number;
-
- //MQTT stuff
- mqttClient: MqttClient | null = null;
-
- // EVE history service handler
- historyService?: any;
-
- // Config
- set_minStep!: number;
- updateRate!: number;
- set_minLux!: number;
- set_maxLux!: number;
- scanDuration!: number;
- deviceLogging!: string;
- deviceRefreshRate!: number;
-
- // Connection
- private readonly OpenAPI: boolean;
- private readonly BLE: boolean;
+ private LightSensor?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ CurrentAmbientLightLevel: CharacteristicValue;
+ };
+
+ private HumiditySensor?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ CurrentRelativeHumidity: CharacteristicValue;
+ };
+
+ private TemperatureSensor?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ CurrentTemperature: CharacteristicValue;
+ };
+
+ // Updates
+ hubUpdateInProgress!: boolean;
+ doHubUpdate!: Subject;
constructor(
- private readonly platform: SwitchBotPlatform,
- private accessory: PlatformAccessory,
- public device: device & devicesConfig,
+ readonly platform: SwitchBotPlatform,
+ accessory: PlatformAccessory,
+ device: device & devicesConfig,
) {
- this.api = this.platform.api;
- this.log = this.platform.log;
- this.config = this.platform.config;
- this.hap = this.api.hap;
- // Connection
- this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI';
- this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI';
- // default placeholders
- this.deviceLogs(device);
- this.refreshRate(device);
- this.deviceContext();
- this.setupHistoryService(device);
- this.setupMqtt(device);
- this.deviceConfig(device);
-
- this.CurrentRelativeHumidity = accessory.context.CurrentRelativeHumidity;
- this.CurrentTemperature = accessory.context.CurrentTemperature;
+ super(platform, accessory, device);
+ // this is subject we use to track when we need to POST changes to the SwitchBot API
+ this.doHubUpdate = new Subject();
+ this.hubUpdateInProgress = false;
- // Retrieve initial values and updateHomekit
- this.refreshStatus();
-
- // set accessory information
- accessory
- .getService(this.hap.Service.AccessoryInformation)!
- .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
- .setCharacteristic(this.hap.Characteristic.Model, accessory.context.model)
- .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId)
- .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
-
- // Temperature Sensor Service
+ // Initialize Temperature Sensor Service
if (device.hub?.hide_temperature) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`);
- this.temperatureService = this.accessory.getService(this.hap.Service.TemperatureSensor);
- accessory.removeService(this.temperatureService!);
- } else if (!this.temperatureService) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`);
- const temperatureService = `${accessory.displayName} Temperature Sensor`;
- (this.temperatureService = this.accessory.getService(this.hap.Service.TemperatureSensor)
- || this.accessory.addService(this.hap.Service.TemperatureSensor)), temperatureService;
-
- this.temperatureService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Temperature Sensor`);
- if (!this.temperatureService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.temperatureService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Temperature Sensor`);
+ if (this.TemperatureSensor) {
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`);
+ this.TemperatureSensor.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service;
+ accessory.removeService(this.TemperatureSensor.Service);
}
- this.temperatureService
+ } else {
+ accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {};
+ this.TemperatureSensor = {
+ Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`,
+ Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service,
+ CurrentTemperature: accessory.context.CurrentTemperature ?? 0,
+ };
+ accessory.context.TemperatureSensor = this.TemperatureSensor as object;
+
+ // Initialize Temperature Sensor Characteristic
+ this.TemperatureSensor.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.TemperatureSensor.Name)
.getCharacteristic(this.hap.Characteristic.CurrentTemperature)
.setProps({
unit: Units['CELSIUS'],
@@ -110,312 +74,128 @@ export class Hub {
minStep: 0.1,
})
.onGet(() => {
- return this.CurrentTemperature!;
+ return this.TemperatureSensor!.CurrentTemperature;
});
- } else {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`);
}
- // Humidity Sensor Service
+ // Initialize Humidity Sensor Service
if (device.hub?.hide_humidity) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`);
- this.humidityService = this.accessory.getService(this.hap.Service.HumiditySensor);
- accessory.removeService(this.humidityService!);
- } else if (!this.humidityService) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`);
- const humidityService = `${accessory.displayName} Humidity Sensor`;
- (this.humidityService = this.accessory.getService(this.hap.Service.HumiditySensor)
- || this.accessory.addService(this.hap.Service.HumiditySensor)), humidityService;
-
- this.humidityService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Humidity Sensor`);
- if (!this.humidityService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.humidityService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Humidity Sensor`);
+ if (this.HumiditySensor) {
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`);
+ this.HumiditySensor.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service;
+ accessory.removeService(this.HumiditySensor.Service);
}
- this.humidityService
+ } else {
+ accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {};
+ this.HumiditySensor = {
+ Name: accessory.context.HumiditySensor.Name ?? `${accessory.displayName} Humidity Sensor`,
+ Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service,
+ CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 0,
+ };
+ accessory.context.HumiditySensor = this.HumiditySensor as object;
+
+ // Initialize Humidity Sensor Characteristics
+ this.HumiditySensor!.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.HumiditySensor.Name)
.getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity)
.setProps({
minStep: 0.1,
})
.onGet(() => {
- return this.CurrentRelativeHumidity;
+ return this.HumiditySensor!.CurrentRelativeHumidity;
});
- } else {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Added`);
}
- // Light Sensor Service
+ // Initialize Light Sensor Service
if (device.hub?.hide_lightsensor) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`);
- this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor);
- accessory.removeService(this.lightSensorService!);
- } else if (!this.lightSensorService) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Light Sensor Service`);
- const lightSensorService = `${accessory.displayName} Light Sensor`;
- (this.lightSensorService = this.accessory.getService(this.hap.Service.LightSensor)
- || this.accessory.addService(this.hap.Service.LightSensor)), lightSensorService;
-
- this.lightSensorService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Light Sensor`);
- this.lightSensorService.setCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Light Sensor`);
+ if (this.LightSensor) {
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Light Sensor Service`);
+ this.LightSensor.Service = this.accessory.getService(this.hap.Service.LightSensor) as Service;
+ accessory.removeService(this.LightSensor.Service);
+ }
} else {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Light Sensor Service Not Added`);
+ accessory.context.LightSensor = accessory.context.LightSensor ?? {};
+ this.LightSensor = {
+ Name: accessory.context.LightSensor.Name ?? `${accessory.displayName} Light Sensor`,
+ Service: accessory.getService(this.hap.Service.LightSensor) ?? this.accessory.addService(this.hap.Service.LightSensor) as Service,
+ CurrentAmbientLightLevel: accessory.context.CurrentAmbientLightLevel ?? 0.0001,
+ };
+ accessory.context.LightSensor = this.LightSensor as object;
+
+ // Initialize Light Sensor Characteristics
+ this.LightSensor!.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.LightSensor.Name)
+ .getCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel)
+ .setProps({
+ minStep: 1,
+ })
+ .onGet(() => {
+ return this.LightSensor!.CurrentAmbientLightLevel;
+ });
}
+ // Retrieve initial values and updateHomekit
+ this.refreshStatus();
+
// Retrieve initial values and update Homekit
this.updateHomeKitCharacteristics();
// Start an update interval
interval(this.deviceRefreshRate * 1000)
+ .pipe(skipWhile(() => this.hubUpdateInProgress))
.subscribe(async () => {
await this.refreshStatus();
});
//regisiter webhook event handler
- if (this.device.webhook) {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`);
- this.platform.webhookEventHandler[this.device.deviceId] = async (context) => {
- try {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
- if (context.scale === 'CELSIUS') {
- const { temperature, humidity, lightLevel } = context;
- const { CurrentTemperature, CurrentRelativeHumidity, CurrentAmbientLightLevel } = this;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` +
- '(temperature, humidity, lightLevel) = ' +
- `Webhook:(${temperature}, ${humidity}, ${lightLevel}), ` +
- `current:(${CurrentTemperature}, ${CurrentRelativeHumidity}, ${CurrentAmbientLightLevel})`);
- this.CurrentRelativeHumidity = humidity;
- this.CurrentTemperature = temperature;
- this.set_minLux = this.minLux();
- this.set_maxLux = this.maxLux();
- this.spaceBetweenLevels = 19;
- switch (lightLevel) {
- case 1:
- this.CurrentAmbientLightLevel = this.set_minLux;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 2:
- this.CurrentAmbientLightLevel = (this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels;
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel},` +
- ` Calculation: ${(this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels}`,
- );
- break;
- case 3:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 2;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 4:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 3;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 5:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 4;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 6:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 5;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 7:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 6;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 8:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 7;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 9:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 8;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 10:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 9;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 11:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 10;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 12:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 11;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 13:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 12;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 14:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 13;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 15:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 14;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 16:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 15;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 17:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 16;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 18:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 17;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 19:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 18;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 20:
- default:
- this.CurrentAmbientLightLevel = this.set_maxLux;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- }
- this.updateHomeKitCharacteristics();
- }
- } catch (e: any) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
- + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
- }
- };
- }
- }
+ this.registerWebhook(accessory, device);
- /**
- * Parse the device status from the SwitchBot api
- */
- async parseStatus(): Promise {
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLE not supported`);
- } else if (this.OpenAPI && this.platform.config.credentials?.token) {
- await this.openAPIparseStatus();
- } else {
- await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`,
- );
- }
}
- async openAPIparseStatus(): Promise {
+ async openAPIparseStatus(deviceStatus: deviceStatus): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
// CurrentRelativeHumidity
if (!this.device.hub?.hide_humidity) {
- this.CurrentRelativeHumidity = Number(this.OpenAPI_CurrentRelativeHumidity);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`);
+ this.HumiditySensor!.CurrentRelativeHumidity = Number(deviceStatus.body.humidity);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.HumiditySensor!.CurrentRelativeHumidity}%`);
}
// CurrentTemperature
if (!this.device.hub?.hide_temperature) {
- this.CurrentTemperature = Number(this.OpenAPI_CurrentTemperature);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`);
+ this.TemperatureSensor!.CurrentTemperature = Number(deviceStatus.body.temperature);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`);
}
// Brightness
if (!this.device.hub?.hide_lightsensor) {
if (!this.device.curtain?.hide_lightsensor) {
- this.set_minLux = this.minLux();
- this.set_maxLux = this.maxLux();
- this.spaceBetweenLevels = 19;
- switch (this.OpenAPI_CurrentAmbientLightLevel) {
- case 1:
- this.CurrentAmbientLightLevel = this.set_minLux;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 2:
- this.CurrentAmbientLightLevel = (this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels;
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel},` +
- ` Calculation: ${(this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels}`,
- );
- break;
- case 3:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 2;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 4:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 3;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 5:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 4;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 6:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 5;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 7:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 6;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 8:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 7;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 9:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 8;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 10:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 9;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 11:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 10;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 12:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 11;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 13:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 12;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 14:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 13;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 15:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 14;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 16:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 15;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 17:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 16;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 18:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 17;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 19:
- this.CurrentAmbientLightLevel = ((this.set_maxLux - this.set_minLux) / this.spaceBetweenLevels) * 18;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- break;
- case 20:
- default:
- this.CurrentAmbientLightLevel = this.set_maxLux;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel}`);
- }
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${this.OpenAPI_CurrentAmbientLightLevel},` +
- ` CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
- );
+ const set_minLux = this.device.curtain?.set_minLux ?? 1;
+ const set_maxLux = this.device.curtain?.set_maxLux ?? 6001;
+ const spaceBetweenLevels = 19;
+ const lightLevel = deviceStatus.body.lightLevel;
+ await this.getLightLevel(lightLevel, set_minLux, set_maxLux, spaceBetweenLevels);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${deviceStatus.body.lightLevel},`
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
}
if (!this.device.hub?.hide_lightsensor) {
- this.lightSensorService?.setCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
+ this.LightSensor!.Service.setCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel);
}
}
- // FirmwareRevision
- this.FirmwareRevision = this.OpenAPI_FirmwareRevision!;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
+ // Firmware Version
+ const version = deviceStatus.body.version?.toString();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
+ if (deviceStatus.body.version) {
+ const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
+ this.accessory
+ .getService(this.hap.Service.AccessoryInformation)!
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
+ .updateValue(deviceVersion);
+ this.accessory.context.deviceVersion = deviceVersion;
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
+ }
}
async refreshStatus(): Promise {
@@ -432,21 +212,16 @@ export class Hub {
async openAPIRefreshStatus(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
try {
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, {
- headers: this.platform.generateHeaders(),
- });
+ const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries,
+ `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() });
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
const deviceStatus: any = await body.json();
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
- this.OpenAPI_CurrentTemperature = deviceStatus.body.temperature;
- this.OpenAPI_CurrentRelativeHumidity = deviceStatus.body.humidity;
- this.OpenAPI_CurrentAmbientLightLevel = deviceStatus.body.lightLevel;
- this.OpenAPI_FirmwareRevision = deviceStatus.body.version;
- this.openAPIparseStatus();
+ this.openAPIparseStatus(deviceStatus);
this.updateHomeKitCharacteristics();
} else {
this.statusCode(statusCode);
@@ -454,10 +229,47 @@ export class Hub {
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
+ }
+ }
+
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
+ if (device.webhook) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`);
+ this.platform.webhookEventHandler[this.device.deviceId] = async (context) => {
+ try {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
+ const { temperature, humidity, lightLevel } = context;
+ const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined };
+ const { CurrentAmbientLightLevel } = this.LightSensor || { CurrentAmbientLightLevel: undefined };
+ const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined };
+ if (context.scale !== 'CELCIUS' && device.hub?.convertUnitTo === undefined) {
+ this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} received a non-CELCIUS Webhook scale: `
+ + `${context.scale}, Use the *convertUnitsTo* config under Hub settings, if displaying incorrectly in HomeKit.`);
+ }
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` +
+ '(scale, temperature, humidity, lightLevel) = ' +
+ `Webhook:(${context.scale}, ${convertUnits(temperature, context.scale, device.hub?.convertUnitTo)}, ${humidity}, ${lightLevel}), `
+ + `current:(${CurrentTemperature}, ${CurrentRelativeHumidity}, ${CurrentAmbientLightLevel})`);
+ if (!this.device.hub?.hide_humidity) {
+ this.HumiditySensor!.CurrentRelativeHumidity = humidity;
+ }
+ if (!this.device.hub?.hide_temperature) {
+ this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.hub?.convertUnitTo);
+ }
+ if (!this.device.hub?.hide_lightsensor) {
+ const set_minLux = this.device.curtain?.set_minLux ?? 1;
+ const set_maxLux = this.device.curtain?.set_maxLux ?? 6001;
+ const spaceBetweenLevels = 19;
+ await this.getLightLevel(lightLevel, set_minLux, set_maxLux, spaceBetweenLevels);
+ }
+ this.updateHomeKitCharacteristics();
+ } catch (e: any) {
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
+ }
+ };
}
}
@@ -471,56 +283,58 @@ export class Hub {
// CurrentRelativeHumidity
if (!this.device.hub?.hide_humidity) {
- if (this.CurrentRelativeHumidity === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
+ if (this.HumiditySensor!.CurrentRelativeHumidity === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`);
} else {
if (this.device.mqttURL) {
- mqttmessage.push(`"humidity": ${this.CurrentRelativeHumidity}`);
+ mqttmessage.push(`"humidity": ${this.HumiditySensor!.CurrentRelativeHumidity}`);
}
if (this.device.history) {
- entry['humidity'] = this.CurrentRelativeHumidity;
+ entry['humidity'] = this.HumiditySensor!.CurrentRelativeHumidity;
}
- this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidity;
- this.humidityService?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity);
+ this.accessory.context.CurrentRelativeHumidity = this.HumiditySensor!.CurrentRelativeHumidity;
+ this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity,
+ this.HumiditySensor!.CurrentRelativeHumidity);
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} `
- + `updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
+ + `updateCharacteristic CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`);
}
}
// CurrentTemperature
if (!this.device.hub?.hide_temperature) {
- if (this.CurrentTemperature === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`);
+ if (this.TemperatureSensor!.CurrentTemperature === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`);
} else {
if (this.device.mqttURL) {
- mqttmessage.push(`"temperature": ${this.CurrentTemperature}`);
+ mqttmessage.push(`"temperature": ${this.TemperatureSensor!.CurrentTemperature}`);
}
if (this.device.history) {
- entry['temp'] = this.CurrentTemperature;
+ entry['temp'] = this.TemperatureSensor!.CurrentTemperature;
}
- this.accessory.context.CurrentTemperature = this.CurrentTemperature;
- this.temperatureService?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.CurrentTemperature);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`);
+ this.accessory.context.CurrentTemperature = this.TemperatureSensor!.CurrentTemperature;
+ this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor!.CurrentTemperature);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`);
}
}
// CurrentAmbientLightLevel
if (!this.device.hub?.hide_lightsensor) {
- if (this.CurrentAmbientLightLevel === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`);
+ if (this.LightSensor!.CurrentAmbientLightLevel === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
} else {
if (this.device.mqttURL) {
- mqttmessage.push(`"light": ${this.CurrentAmbientLightLevel}`);
+ mqttmessage.push(`"light": ${this.LightSensor!.CurrentAmbientLightLevel}`);
}
if (this.device.history) {
- entry['lux'] = this.CurrentAmbientLightLevel;
+ entry['lux'] = this.LightSensor!.CurrentAmbientLightLevel;
}
- this.accessory.context.CurrentAmbientLightLevel = this.CurrentAmbientLightLevel;
- this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.CurrentAmbientLightLevel);
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} `
- + `updateCharacteristic CurrentAmbientLightLevel: ${this.CurrentAmbientLightLevel}`,
- );
+ this.accessory.context.CurrentAmbientLightLevel = this.LightSensor!.CurrentAmbientLightLevel;
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, this.LightSensor!.CurrentAmbientLightLevel);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `updateCharacteristic CurrentAmbientLightLevel: ${this.LightSensor!.CurrentAmbientLightLevel}`);
}
}
@@ -528,7 +342,7 @@ export class Hub {
if (this.device.mqttURL) {
this.mqttPublish(`{${mqttmessage.join(',')}}`);
}
- if (Number(this.CurrentRelativeHumidity) > 0) {
+ if (Number(this.HumiditySensor!.CurrentRelativeHumidity) > 0) {
// reject unreliable data
if (this.device.history) {
this.historyService?.addEntry(entry);
@@ -536,301 +350,117 @@ export class Hub {
}
}
- /*
- * Publish MQTT message for topics of
- * 'homebridge-switchbot/meter/xx:xx:xx:xx:xx:xx'
- */
- mqttPublish(message: any) {
- const mac = this.device.deviceId
- ?.toLowerCase()
- .match(/[\s\S]{1,2}/g)
- ?.join(':');
- const options = this.device.mqttPubOptions || {};
- this.mqttClient?.publish(`homebridge-switchbot/hub/${mac}`, `${message}`, options);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${message} options:${JSON.stringify(options)}`);
- }
-
- /*
- * Setup MQTT hadler if URL is specified.
- */
- async setupMqtt(device: device & devicesConfig): Promise {
- if (device.mqttURL) {
- try {
- const { connectAsync } = asyncmqtt;
- this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`);
- this.mqttClient.on('error', (e: Error) => {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`);
- });
- } catch (e) {
- this.mqttClient = null;
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`);
+ async offlineOff(): Promise {
+ if (this.device.offline) {
+ if (!this.device.hub?.hide_temperature) {
+ this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.accessory.context.CurrentTemperature);
+ }
+ if (!this.device.hub?.hide_humidity) {
+ this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity,
+ this.accessory.context.CurrentRelativeHumidity);
+ }
+ if (!this.device.hub?.hide_lightsensor) {
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel,
+ this.accessory.context.CurrentAmbientLightLevel);
}
}
}
- /*
- * Setup EVE history graph feature if enabled.
- */
- async setupHistoryService(device: device & devicesConfig): Promise {
- const mac = this.device
- .deviceId!.match(/.{1,2}/g)!
- .join(':')
- .toLowerCase();
- this.historyService = device.history
- ? new this.platform.fakegatoAPI('custom', this.accessory, {
- log: this.platform.log,
- storage: 'fs',
- filename: `${hostname().split('.')[0]}_${mac}_persist.json`,
- })
- : null;
+ async apiError(e: any): Promise {
+ if (!this.device.hub?.hide_temperature) {
+ this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e);
+ }
+ if (!this.device.hub?.hide_humidity) {
+ this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e);
+ }
+ if (!this.device.hub?.hide_lightsensor) {
+ this.LightSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e);
+ }
}
- async statusCode(statusCode: number): Promise {
- switch (statusCode) {
- case 151:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
+ async getLightLevel(lightLevel: any, set_minLux: number, set_maxLux: number, spaceBetweenLevels: number) {
+ switch (lightLevel) {
+ case 1:
+ this.LightSensor!.CurrentAmbientLightLevel = set_minLux;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 152:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
+ case 2:
+ this.LightSensor!.CurrentAmbientLightLevel = (set_maxLux - set_minLux) / spaceBetweenLevels;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel},`
+ + ` Calculation: ${(set_maxLux - set_minLux) / spaceBetweenLevels}`);
break;
- case 160:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
+ case 3:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 2;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 161:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
- this.offlineOff();
+ case 4:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 3;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 171:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` +
- `Hub: ${this.device.hubDeviceId}`,
- );
- this.offlineOff();
+ case 5:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 4;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 190:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
- ` Or command format is invalid, statusCode: ${statusCode}`,
- );
+ case 6:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 5;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 100:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
+ case 7:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 6;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 200:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
+ case 8:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 7;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 400:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. `
- + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`);
+ case 9:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 8;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 401:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, `
- + `but the request has not been authenticated, statusCode: ${statusCode}`);
+ case 10:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 9;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 403:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not `
- + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`);
+ case 11:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 10;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 404:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, `
- + `statusCode: ${statusCode}`);
+ case 12:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 11;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 406:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via `
- + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`);
+ case 13:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 12;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 415:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType `
- + `header that is not supported by the server, statusCode: ${statusCode}`);
+ case 14:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 13;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 422:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, The client has made a valid request, `
- + `but the server cannot process it. This is often used for APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
+ case 15:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 14;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 429:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of `
- + `requests allowed for a given time window, statusCode: ${statusCode}`);
+ case 16:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 15;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
- case 500:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings `
- + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`);
+ case 17:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 16;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
break;
+ case 18:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 17;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
+ break;
+ case 19:
+ this.LightSensor!.CurrentAmbientLightLevel = ((set_maxLux - set_minLux) / spaceBetweenLevels) * 18;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
+ break;
+ case 20:
default:
- this.infoLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` +
- `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`,
- );
- }
- }
-
- async offlineOff(): Promise {
- if (this.device.offline) {
- await this.deviceContext();
- await this.updateHomeKitCharacteristics();
- }
- }
-
- async apiError(e: any): Promise {
- if (!this.device.hub?.hide_temperature) {
- this.temperatureService?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e);
- }
- if (!this.device.hub?.hide_humidity) {
- this.humidityService?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e);
- }
- if (!this.device.hub?.hide_lightsensor) {
- this.lightSensorService?.updateCharacteristic(this.hap.Characteristic.CurrentAmbientLightLevel, e);
- }
- }
-
- minLux(): number {
- if (this.device.curtain?.set_minLux) {
- this.set_minLux = this.device.curtain?.set_minLux;
- } else {
- this.set_minLux = 1;
+ this.LightSensor!.CurrentAmbientLightLevel = set_maxLux;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} LightLevel: ${lightLevel}`);
}
- return this.set_minLux;
- }
-
- maxLux(): number {
- if (this.device.curtain?.set_maxLux) {
- this.set_maxLux = this.device.curtain?.set_maxLux;
- } else {
- this.set_maxLux = 6001;
- }
- return this.set_maxLux;
- }
-
- async deviceContext() {
- if (this.CurrentRelativeHumidity === undefined) {
- this.CurrentRelativeHumidity = 0;
- } else {
- this.CurrentRelativeHumidity = this.accessory.context.CurrentRelativeHumidity;
- }
- if (this.CurrentTemperature === undefined) {
- this.CurrentTemperature = 0;
- } else {
- this.CurrentTemperature = this.accessory.context.CurrentTemperature;
- }
- if (this.CurrentAmbientLightLevel === undefined) {
- this.CurrentAmbientLightLevel = this.set_minLux;
- } else {
- this.CurrentAmbientLightLevel = this.accessory.context.CurrentAmbientLightLevel;
- }
- if (this.FirmwareRevision === undefined) {
- this.FirmwareRevision = this.platform.version;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
- }
- }
-
- async refreshRate(device: device & devicesConfig): Promise {
- // refreshRate
- if (device.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
- } else if (this.platform.config.options!.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
- }
- // updateRate
- if (device?.curtain?.updateRate) {
- this.updateRate = device?.curtain?.updateRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Curtain updateRate: ${this.updateRate}`);
- } else {
- this.updateRate = 7;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default Curtain updateRate: ${this.updateRate}`);
- }
- }
-
- async deviceConfig(device: device & devicesConfig): Promise {
- let config = {};
- if (device.hub) {
- config = device.hub;
- }
- if (device.connectionType !== undefined) {
- config['connectionType'] = device.connectionType;
- }
- if (device.external !== undefined) {
- config['external'] = device.external;
- }
- if (device.logging !== undefined) {
- config['logging'] = device.logging;
- }
- if (device.refreshRate !== undefined) {
- config['refreshRate'] = device.refreshRate;
- }
- if (device.webhook !== undefined) {
- config['webhook'] = device.webhook;
- }
- if (Object.entries(config).length !== 0) {
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
- }
- }
-
- async deviceLogs(device: device & devicesConfig): Promise {
- if (this.platform.debugMode) {
- this.deviceLogging = this.accessory.context.logging = 'debugMode';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
- } else if (device.logging) {
- this.deviceLogging = this.accessory.context.logging = device.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
- } else if (this.platform.config.options?.logging) {
- this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
- } else {
- this.deviceLogging = this.accessory.context.logging = 'standard';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
- }
- }
-
- /**
- * Logging for Device
- */
- infoLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.info(String(...log));
- }
- }
-
- warnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.warn(String(...log));
- }
- }
-
- debugWarnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.warn('[DEBUG]', String(...log));
- }
- }
- }
-
- errorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.error(String(...log));
- }
- }
-
- debugErrorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.error('[DEBUG]', String(...log));
- }
- }
- }
-
- debugLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging === 'debug') {
- this.platform.log.info('[DEBUG]', String(...log));
- } else {
- this.platform.log.debug(String(...log));
- }
- }
- }
-
- enablingDeviceLogging(): boolean {
- return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard';
}
}
diff --git a/src/device/humidifier.ts b/src/device/humidifier.ts
index b11d1fe2..945ea9a8 100644
--- a/src/device/humidifier.ts
+++ b/src/device/humidifier.ts
@@ -1,119 +1,74 @@
import { request } from 'undici';
-import { sleep } from '../utils.js';
+import { deviceBase } from './device.js';
import { interval, Subject } from 'rxjs';
-import { SwitchBotPlatform } from '../platform.js';
+import { Devices } from '../settings.js';
+import { convertUnits } from '../utils.js';
import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
-import { Service, PlatformAccessory, CharacteristicValue, API, Logging, HAP } from 'homebridge';
-import { device, devicesConfig, serviceData, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js';
+
+import type { SwitchBotPlatform } from '../platform.js';
+import type { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
+import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js';
/**
* Platform Accessory
* An instance of this class is created for each accessory your platform registers
* Each accessory may expose multiple services of different service types.
*/
-export class Humidifier {
- public readonly api: API;
- public readonly log: Logging;
- public readonly config!: SwitchBotPlatformConfig;
- protected readonly hap: HAP;
+export class Humidifier extends deviceBase {
// Services
- humidifierService: Service;
- temperatureservice?: Service;
-
- // Characteristic Values
- Active!: CharacteristicValue;
- WaterLevel!: CharacteristicValue;
- FirmwareRevision!: CharacteristicValue;
- CurrentTemperature!: CharacteristicValue;
- CurrentRelativeHumidity!: CharacteristicValue;
- TargetHumidifierDehumidifierState!: CharacteristicValue;
- CurrentHumidifierDehumidifierState!: CharacteristicValue;
- RelativeHumidityHumidifierThreshold!: CharacteristicValue;
-
- // OpenAPI
- OpenAPI_Active: deviceStatus['power'];
- OpenAPI_WaterLevel: deviceStatus['lackWater'];
- OpenAPI_FirmwareRevision: deviceStatus['version'];
- OpenAPI_CurrentTemperature: deviceStatus['temperature'];
- OpenAPI_CurrentRelativeHumidity: deviceStatus['humidity'];
- OpenAPI_CurrentHumidifierDehumidifierState: deviceStatus['auto'];
- OpenAPI_RelativeHumidityHumidifierThreshold: deviceStatus['nebulizationEfficiency'];
-
- // BLE Others
- connected?: boolean;
- onState!: serviceData['onState'];
- autoMode!: serviceData['autoMode'];
- percentage!: serviceData['percentage'];
-
- // Config
- set_minStep?: number;
- scanDuration!: number;
- deviceLogging!: string;
- deviceRefreshRate!: number;
+ private HumidifierDehumidifier: {
+ Name: CharacteristicValue;
+ Service: Service;
+ Active: CharacteristicValue;
+ WaterLevel: CharacteristicValue;
+ CurrentRelativeHumidity: CharacteristicValue;
+ TargetHumidifierDehumidifierState: CharacteristicValue;
+ CurrentHumidifierDehumidifierState: CharacteristicValue;
+ RelativeHumidityHumidifierThreshold: CharacteristicValue;
+ };
+
+ private TemperatureSensor?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ CurrentTemperature: CharacteristicValue;
+ };
// Updates
humidifierUpdateInProgress!: boolean;
doHumidifierUpdate!: Subject;
- // Connection
- private readonly OpenAPI: boolean;
- private readonly BLE: boolean;
-
constructor(
- private readonly platform: SwitchBotPlatform,
- private accessory: PlatformAccessory,
- public device: device & devicesConfig,
+ readonly platform: SwitchBotPlatform,
+ accessory: PlatformAccessory,
+ device: device & devicesConfig,
) {
- this.api = this.platform.api;
- this.log = this.platform.log;
- this.config = this.platform.config;
- this.hap = this.api.hap;
- // Connection
- this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI';
- this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI';
- // default placeholders
- this.deviceLogs(device);
- this.scan(device);
- this.refreshRate(device);
- this.deviceContext();
- this.deviceConfig(device);
-
+ super(platform, accessory, device);
// this is subject we use to track when we need to POST changes to the SwitchBot API
this.doHumidifierUpdate = new Subject();
this.humidifierUpdateInProgress = false;
- // Retrieve initial values and updateHomekit
- this.refreshStatus();
-
- // set accessory information
- accessory
- .getService(this.hap.Service.AccessoryInformation)!
- .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
- .setCharacteristic(this.hap.Characteristic.Model, 'W0801800')
- .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId)
- .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
-
- // get the service if it exists, otherwise create a new service
- // you can create multiple services for each accessory
- const humidifierService = `${accessory.displayName} Humidifier`;
- (this.humidifierService = accessory.getService(this.hap.Service.HumidifierDehumidifier)
- || accessory.addService(this.hap.Service.HumidifierDehumidifier)), humidifierService;
-
- this.humidifierService.setCharacteristic(this.hap.Characteristic.Name, accessory.displayName);
- if (!this.humidifierService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.humidifierService.addCharacteristic(this.hap.Characteristic.ConfiguredName, accessory.displayName);
- }
-
- // each service must implement at-minimum the "required characteristics" for the given service type
- // see https://developers.homebridge.io/#/service/HumidifierDehumidifier
-
- // create handlers for required characteristics
- this.humidifierService.setCharacteristic(
- this.hap.Characteristic.CurrentHumidifierDehumidifierState,
- this.CurrentHumidifierDehumidifierState,
- );
-
- this.humidifierService
+ // Initialize the HumidifierDehumidifier Service
+ accessory.context.HumidifierDehumidifier = accessory.context.HumidifierDehumidifier ?? {};
+ this.HumidifierDehumidifier = {
+ Name: accessory.context.HumidifierDehumidifier.Name ?? accessory.displayName,
+ Service: accessory.getService(this.hap.Service.HumidifierDehumidifier)
+ ?? accessory.addService(this.hap.Service.HumidifierDehumidifier) as Service,
+ Active: accessory.context.Active ?? this.hap.Characteristic.Active.ACTIVE,
+ WaterLevel: accessory.context.WaterLevel ?? 100,
+ CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 50,
+ TargetHumidifierDehumidifierState: accessory.context.TargetHumidifierDehumidifierState
+ ?? this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER,
+ CurrentHumidifierDehumidifierState: accessory.context.CurrentHumidifierDehumidifierState
+ ?? this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE,
+ RelativeHumidityHumidifierThreshold: accessory.context.RelativeHumidityHumidifierThreshold ?? 50,
+ };
+ accessory.context.HumidifierDehumidifier = this.HumidifierDehumidifier as object;
+
+ // Initialize the HumidifierDehumidifier Characteristics
+ this.HumidifierDehumidifier.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.HumidifierDehumidifier.Name)
+ .setCharacteristic(this.hap.Characteristic.CurrentHumidifierDehumidifierState,
+ this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState)
.getCharacteristic(this.hap.Characteristic.TargetHumidifierDehumidifierState)
.setProps({
validValueRanges: [0, 1],
@@ -121,36 +76,50 @@ export class Humidifier {
maxValue: 1,
validValues: [0, 1],
})
+ .onGet(() => {
+ return this.HumidifierDehumidifier.TargetHumidifierDehumidifierState;
+ })
.onSet(this.TargetHumidifierDehumidifierStateSet.bind(this));
- this.humidifierService.getCharacteristic(this.hap.Characteristic.Active).onSet(this.ActiveSet.bind(this));
+ this.HumidifierDehumidifier.Service
+ .getCharacteristic(this.hap.Characteristic.Active)
+ .onGet(() => {
+ return this.HumidifierDehumidifier.Active;
+ })
+ .onSet(this.ActiveSet.bind(this));
- this.humidifierService
+ this.HumidifierDehumidifier.Service
.getCharacteristic(this.hap.Characteristic.RelativeHumidityHumidifierThreshold)
.setProps({
validValueRanges: [0, 100],
minValue: 0,
maxValue: 100,
- minStep: this.minStep(),
+ minStep: device.humidifier?.set_minStep ?? 1,
+ })
+ .onGet(() => {
+ return this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold;
})
.onSet(this.RelativeHumidityHumidifierThresholdSet.bind(this));
- // Temperature Sensor Service
- if (device.humidifier?.hide_temperature || this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`);
- this.temperatureservice = this.accessory.getService(this.hap.Service.TemperatureSensor);
- accessory.removeService(this.temperatureservice!);
- } else if (!this.temperatureservice && !this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`);
- const temperatureservice = `${accessory.displayName} Temperature Sensor`;
- (this.temperatureservice = this.accessory.getService(this.hap.Service.TemperatureSensor)
- || this.accessory.addService(this.hap.Service.TemperatureSensor)), temperatureservice;
-
- this.temperatureservice.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Temperature Sensor`);
- if (!this.temperatureservice.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.temperatureservice.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Temperature Sensor`);
+ // Initialize the Temperature Sensor Service
+ if (device.humidifier?.hide_temperature) {
+ if (this.TemperatureSensor) {
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`);
+ this.TemperatureSensor!.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service;
+ accessory.removeService(this.TemperatureSensor!.Service);
}
- this.temperatureservice
+ } else {
+ accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {};
+ this.TemperatureSensor = {
+ Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`,
+ Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service,
+ CurrentTemperature: accessory.context.CurrentTemperature || 30,
+ };
+ accessory.context.TemperatureSensor = this.TemperatureSensor as object;
+
+ // Initialize the Temperature Sensor Characteristics
+ this.TemperatureSensor.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.TemperatureSensor.Name)
.getCharacteristic(this.hap.Characteristic.CurrentTemperature)
.setProps({
validValueRanges: [-273.15, 100],
@@ -159,45 +128,25 @@ export class Humidifier {
minStep: 0.1,
})
.onGet(() => {
- return this.CurrentTemperature;
+ return this.TemperatureSensor!.CurrentTemperature;
});
- } else {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`);
}
+ // Retrieve initial values and updateHomekit
+ this.refreshStatus();
+
// Retrieve initial values and updateHomekit
this.updateHomeKitCharacteristics();
// Start an update interval
interval(this.deviceRefreshRate * 1000)
.pipe(skipWhile(() => this.humidifierUpdateInProgress))
- .subscribe(() => {
- this.refreshStatus();
+ .subscribe(async () => {
+ await this.refreshStatus();
});
//regisiter webhook event handler
- if (this.device.webhook) {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`);
- this.platform.webhookEventHandler[this.device.deviceId] = async (context) => {
- try {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
- if (context.scale === 'CELSIUS') {
- const { temperature, humidity } = context;
- const { CurrentTemperature, CurrentRelativeHumidity } = this;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` +
- '(temperature, humidity) = ' +
- `Webhook:(${temperature}, ${humidity}), ` +
- `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`);
- this.CurrentRelativeHumidity = humidity;
- this.CurrentTemperature = temperature;
- this.updateHomeKitCharacteristics();
- }
- } catch (e: any) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
- + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
- }
- };
- }
+ this.registerWebhook(accessory, device);
// Watch for Humidifier change events
// We put in a debounce of 100ms so we don't make duplicate calls
@@ -206,121 +155,112 @@ export class Humidifier {
tap(() => {
this.humidifierUpdateInProgress = true;
}),
- debounceTime(this.platform.config.options!.pushRate! * 1000),
+ debounceTime(this.devicePushRate * 1000),
)
.subscribe(async () => {
try {
await this.pushChanges();
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,` +
- ` Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushChanges with ${this.device.connectionType} Connection,`
+ + ` Error Message: ${JSON.stringify(e.message)}`);
}
this.humidifierUpdateInProgress = false;
});
}
- /**
- * Parse the device status from the SwitchBot api
- */
- async parseStatus(): Promise {
- if (!this.device.enableCloudService && this.OpenAPI) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`);
- } else if (this.BLE) {
- await this.BLEparseStatus();
- } else if (this.OpenAPI && this.platform.config.credentials?.token) {
- await this.openAPIparseStatus();
- } else {
- await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`,
- );
- }
- }
-
- async BLEparseStatus(): Promise {
+ async BLEparseStatus(serviceData: serviceData): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
+ // Target Humidifier Dehumidifier State
+ if (serviceData.autoMode) {
+ this.HumidifierDehumidifier.TargetHumidifierDehumidifierState = this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER;
+ }
// Current Relative Humidity
- this.CurrentRelativeHumidity = this.percentage!;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
+ this.HumidifierDehumidifier.CurrentRelativeHumidity = serviceData.percentage!;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentRelativeHumidity: ${this.HumidifierDehumidifier.CurrentRelativeHumidity}`);
// Active
- if (this.onState) {
- this.Active = this.hap.Characteristic.Active.ACTIVE;
+ if (serviceData.onState) {
+ this.HumidifierDehumidifier.Active = this.hap.Characteristic.Active.ACTIVE;
} else {
- this.Active = this.hap.Characteristic.Active.INACTIVE;
+ this.HumidifierDehumidifier.Active = this.hap.Characteristic.Active.INACTIVE;
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.Active}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.HumidifierDehumidifier.Active}`);
}
- async openAPIparseStatus(): Promise {
+ async openAPIparseStatus(deviceStatus: deviceStatus): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
// Current Relative Humidity
- this.CurrentRelativeHumidity = this.OpenAPI_CurrentTemperature!;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
+ this.HumidifierDehumidifier.CurrentRelativeHumidity = deviceStatus.body.temperature!;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentRelativeHumidity: ${this.HumidifierDehumidifier.CurrentRelativeHumidity}`);
// Current Temperature
if (!this.device.humidifier?.hide_temperature) {
- this.CurrentTemperature = this.OpenAPI_CurrentTemperature!;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`);
+ this.TemperatureSensor!.CurrentTemperature = deviceStatus.body.temperature!;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`);
}
// Target Humidifier Dehumidifier State
- switch (this.OpenAPI_CurrentHumidifierDehumidifierState) {
+ switch (deviceStatus.body.auto) {
case true:
- this.TargetHumidifierDehumidifierState = this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER;
- this.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING;
- this.RelativeHumidityHumidifierThreshold = this.CurrentRelativeHumidity;
+ this.HumidifierDehumidifier.TargetHumidifierDehumidifierState =
+ this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER;
+ this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING;
+ this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold = this.HumidifierDehumidifier.CurrentRelativeHumidity;
break;
default:
- this.TargetHumidifierDehumidifierState = this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER;
- if (this.OpenAPI_RelativeHumidityHumidifierThreshold! > 100) {
- this.RelativeHumidityHumidifierThreshold = 100;
+ this.HumidifierDehumidifier.TargetHumidifierDehumidifierState = this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER;
+ if (deviceStatus.body.nebulizationEfficiency! > 100) {
+ this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold = 100;
} else {
- this.RelativeHumidityHumidifierThreshold = this.OpenAPI_RelativeHumidityHumidifierThreshold!;
+ this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold = deviceStatus.body.nebulizationEfficiency!;
}
- if (this.CurrentRelativeHumidity > this.RelativeHumidityHumidifierThreshold) {
- this.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.IDLE;
- } else if (this.Active === this.hap.Characteristic.Active.INACTIVE) {
- this.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE;
+ if (this.HumidifierDehumidifier.CurrentRelativeHumidity > this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold) {
+ this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.IDLE;
+ } else if (this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.INACTIVE) {
+ this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE;
} else {
- this.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING;
+ this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING;
}
}
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` + ` TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}`,
- );
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` +
- ` RelativeHumidityHumidifierThreshold: ${this.RelativeHumidityHumidifierThreshold}`,
- );
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` + ` CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` TargetHumidifierDehumidifierState: ${this.HumidifierDehumidifier.TargetHumidifierDehumidifierState}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` RelativeHumidityHumidifierThreshold: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentHumidifierDehumidifierState: ${this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState}`);
// Active
- switch (this.OpenAPI_Active) {
+ switch (deviceStatus.body.power) {
case 'on':
- this.Active = this.hap.Characteristic.Active.ACTIVE;
+ this.HumidifierDehumidifier.Active = this.hap.Characteristic.Active.ACTIVE;
break;
default:
- this.Active = this.hap.Characteristic.Active.INACTIVE;
+ this.HumidifierDehumidifier.Active = this.hap.Characteristic.Active.INACTIVE;
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.Active}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.HumidifierDehumidifier.Active}`);
// Water Level
- if (this.OpenAPI_WaterLevel) {
- this.WaterLevel = 0;
+ if (deviceStatus.body.lackWater) {
+ this.HumidifierDehumidifier.WaterLevel = 0;
} else {
- this.WaterLevel = 100;
+ this.HumidifierDehumidifier.WaterLevel = 100;
}
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} WaterLevel: ${this.WaterLevel}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} WaterLevel: ${this.HumidifierDehumidifier.WaterLevel}`);
- // FirmwareRevision
- this.FirmwareRevision = this.OpenAPI_FirmwareRevision!;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
+ // Firmware Version
+ const version = deviceStatus.body.version?.toString();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
+ if (deviceStatus.body.version) {
+ const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
+ this.accessory
+ .getService(this.hap.Service.AccessoryInformation)!
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
+ .updateValue(deviceVersion);
+ this.accessory.context.deviceVersion = deviceVersion;
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
+ }
}
- /**
- * Asks the SwitchBot API for the latest device information
- */
async refreshStatus(): Promise {
if (!this.device.enableCloudService && this.OpenAPI) {
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} refreshStatus enableCloudService: ${this.device.enableCloudService}`);
@@ -330,10 +270,8 @@ export class Humidifier {
await this.openAPIRefreshStatus();
} else {
await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` +
- ` ${this.device.connectionType}, refreshStatus will not happen.`,
- );
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
}
}
@@ -350,103 +288,43 @@ export class Humidifier {
// Start to monitor advertisement packets
(async () => {
// Start to monitor advertisement packets
- await switchbot.startScan({
- model: 'e',
- id: this.device.bleMac,
- });
+ await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac });
// Set an event handler
switchbot.onadvertisement = (ad: any) => {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`);
- if (this.device.bleMac === ad.address && ad.serviceData.model === 'e') {
+ if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.autoMode = ad.serviceData.autoMode;
- this.onState = ad.serviceData.onState;
- this.percentage = ad.serviceData.percentage > 100 ? 100 : ad.serviceData.percentage;
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
}
};
- // Wait 1 seconds
+ // Wait 10 seconds
await switchbot.wait(this.scanDuration * 1000);
// Stop to monitor
await switchbot.stopScan();
// Update HomeKit
- await this.BLEparseStatus();
+ await this.BLEparseStatus(switchbot.onadvertisement.serviceData);
await this.updateHomeKitCharacteristics();
})();
- /*if (switchbot !== false) {
- switchbot
- .startScan({
- model: 'e',
- id: this.device.bleMac,
- })
- .then(async () => {
- // Set an event handler
- switchbot.onadvertisement = async (ad: ad) => {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` +
- ` BLE Address Found: ${ad.address}`,
- );
- this.autoMode = ad.serviceData.autoMode;
- this.onState = ad.serviceData.onState;
- this.percentage = ad.serviceData.percentage;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName},` +
- `autoMode: ${ad.serviceData.autoMode}, onState: ${ad.serviceData.onState}, percentage: ${ad.serviceData.percentage}`,
- );
-
- if (ad.serviceData) {
- this.connected = true;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.connected}`);
- await this.stopScanning(switchbot);
- } else {
- this.connected = false;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.connected}`);
- }
- };
- // Wait
- return await sleep(this.scanDuration * 1000);
- })
- .then(async () => {
- // Stop to monitor
- await this.stopScanning(switchbot);
- })
- .catch(async (e: any) => {
- this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
- await this.BLERefreshConnection(switchbot);
- });
- } else {
+ if (switchbot === undefined) {
await this.BLERefreshConnection(switchbot);
- }*/
+ }
}
async openAPIRefreshStatus(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
try {
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, {
- headers: this.platform.generateHeaders(),
- });
+ const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries,
+ `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() });
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
const deviceStatus: any = await body.json();
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
- this.OpenAPI_CurrentHumidifierDehumidifierState = deviceStatus.body.auto;
- this.OpenAPI_Active = deviceStatus.body.power;
- this.OpenAPI_WaterLevel = deviceStatus.body.lackWater;
- this.OpenAPI_CurrentRelativeHumidity = deviceStatus.body.humidity;
- this.OpenAPI_CurrentTemperature = deviceStatus.body.temperature;
- this.OpenAPI_RelativeHumidityHumidifierThreshold = deviceStatus.body.nebulizationEfficiency;
- this.OpenAPI_FirmwareRevision = deviceStatus.body.version;
- this.openAPIparseStatus();
+ this.openAPIparseStatus(deviceStatus);
this.updateHomeKitCharacteristics();
} else {
this.statusCode(statusCode);
@@ -454,10 +332,32 @@ export class Humidifier {
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
+ }
+ }
+
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
+ if (device.webhook) {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`);
+ this.platform.webhookEventHandler[device.deviceId] = async (context) => {
+ try {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
+ const { temperature, humidity } = context;
+ const { CurrentRelativeHumidity } = this.HumidifierDehumidifier;
+ const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined };
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} (temperature, humidity) = Webhook:(${convertUnits(temperature,
+ context.scale, device.iosensor?.convertUnitTo)}, ${humidity}), current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`);
+ this.HumidifierDehumidifier.CurrentRelativeHumidity = humidity;
+ if (!device.humidifier?.hide_temperature) {
+ this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo);
+ }
+ this.updateHomeKitCharacteristics();
+ } catch (e: any) {
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} `
+ + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
+ }
+ };
}
}
@@ -473,9 +373,8 @@ export class Humidifier {
await this.openAPIpushChanges();
} else {
await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, pushChanges will not happen.`,
- );
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, pushChanges will not happen.`);
}
interval(5000)
.pipe(take(1))
@@ -500,18 +399,18 @@ export class Humidifier {
id: this.device.bleMac,
})
.then(async (device_list: any) => {
- this.infoLog(`${this.accessory.displayName} Target Position: ${this.Active}`);
- return await device_list[0].percentage(this.RelativeHumidityHumidifierThreshold);
+ this.infoLog(`${this.accessory.displayName} Active: ${this.HumidifierDehumidifier.Active}`);
+ return await device_list[0].percentage(this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold);
})
.then(() => {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Done.`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `Active: ${this.HumidifierDehumidifier.Active} sent over BLE, sent successfully`);
})
.catch(async (e: any) => {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed BLEpushChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
await this.BLEPushConnection();
});
}
@@ -519,13 +418,14 @@ export class Humidifier {
async openAPIpushChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIpushChanges`);
if (
- this.TargetHumidifierDehumidifierState === this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER &&
- this.Active === this.hap.Characteristic.Active.ACTIVE
+ this.HumidifierDehumidifier.TargetHumidifierDehumidifierState === this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER &&
+ this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.ACTIVE
) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing Manual: ${this.RelativeHumidityHumidifierThreshold}!`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` Pushing Manual: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}!`);
const bodyChange = JSON.stringify({
command: 'setMode',
- parameter: `${this.RelativeHumidityHumidifierThreshold}`,
+ parameter: `${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}`,
commandType: 'command',
});
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Sending request to SwitchBot API, body: ${bodyChange},`);
@@ -543,20 +443,21 @@ export class Humidifier {
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
} else {
this.statusCode(statusCode);
this.statusCode(deviceStatus.statusCode);
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIpushChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
}
} else if (
- this.TargetHumidifierDehumidifierState === this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER &&
- this.Active === this.hap.Characteristic.Active.ACTIVE
+ this.HumidifierDehumidifier.TargetHumidifierDehumidifierState ===
+ this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER &&
+ this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.ACTIVE
) {
await this.pushAutoChanges();
} else {
@@ -570,8 +471,9 @@ export class Humidifier {
async pushAutoChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushAutoChanges`);
if (
- this.TargetHumidifierDehumidifierState === this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER &&
- this.Active === this.hap.Characteristic.Active.ACTIVE
+ this.HumidifierDehumidifier.TargetHumidifierDehumidifierState ===
+ this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER &&
+ this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.ACTIVE
) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing Auto`);
const bodyChange = JSON.stringify({
@@ -594,22 +496,20 @@ export class Humidifier {
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
} else {
this.statusCode(statusCode);
this.statusCode(deviceStatus.statusCode);
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushAutoChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushAutoChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
}
} else {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} No pushAutoChanges.` +
- `TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}, Active: ${this.Active}`,
- );
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushAutoChanges. TargetHumidifierDehumidifierState:`
+ + ` ${this.HumidifierDehumidifier.TargetHumidifierDehumidifierState}, Active: ${this.HumidifierDehumidifier.Active}`);
}
}
@@ -618,7 +518,7 @@ export class Humidifier {
*/
async pushActiveChanges(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} pushActiveChanges`);
- if (this.Active === this.hap.Characteristic.Active.INACTIVE) {
+ if (this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.INACTIVE) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Pushing Off`);
const bodyChange = JSON.stringify({
command: 'turnOff',
@@ -640,19 +540,19 @@ export class Humidifier {
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
+ this.successLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ + `request to SwitchBot API, body: ${JSON.stringify(JSON.parse(bodyChange))} sent successfully`);
} else {
this.statusCode(statusCode);
this.statusCode(deviceStatus.statusCode);
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed pushActiveChanges with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed pushActiveChanges with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
}
} else {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushActiveChanges. Active: ${this.Active}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No pushActiveChanges. Active: ${this.HumidifierDehumidifier.Active}`);
}
}
@@ -660,13 +560,13 @@ export class Humidifier {
* Handle requests to set the "Active" characteristic
*/
async ActiveSet(value: CharacteristicValue): Promise {
- if (this.Active === this.accessory.context.Active) {
+ if (this.HumidifierDehumidifier.Active === this.accessory.context.Active) {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} No Changes, Set Active: ${value}`);
} else {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set Active: ${value}`);
}
- this.Active = value;
+ this.HumidifierDehumidifier.Active = value;
this.doHumidifierUpdate.next();
}
@@ -674,13 +574,13 @@ export class Humidifier {
* Handle requests to set the "Target Humidifier Dehumidifier State" characteristic
*/
async TargetHumidifierDehumidifierStateSet(value: CharacteristicValue): Promise {
- if (this.Active === this.hap.Characteristic.Active.ACTIVE) {
+ if (this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.ACTIVE) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetHumidifierDehumidifierState: ${value}`);
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set TargetHumidifierDehumidifierState: ${value}`);
}
- this.TargetHumidifierDehumidifierState = value;
+ this.HumidifierDehumidifier.TargetHumidifierDehumidifierState = value;
this.doHumidifierUpdate.next();
}
@@ -688,16 +588,16 @@ export class Humidifier {
* Handle requests to set the "Relative Humidity Humidifier Threshold" characteristic
*/
async RelativeHumidityHumidifierThresholdSet(value: CharacteristicValue): Promise {
- if (this.Active === this.hap.Characteristic.Active.ACTIVE) {
+ if (this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.ACTIVE) {
this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} Set RelativeHumidityHumidifierThreshold: ${value}`);
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Set RelativeHumidityHumidifierThreshold: ${value}`);
}
- this.RelativeHumidityHumidifierThreshold = value;
- if (this.Active === this.hap.Characteristic.Active.INACTIVE) {
- this.Active = this.hap.Characteristic.Active.ACTIVE;
- this.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.IDLE;
+ this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold = value;
+ if (this.HumidifierDehumidifier.Active === this.hap.Characteristic.Active.INACTIVE) {
+ this.HumidifierDehumidifier.Active = this.hap.Characteristic.Active.ACTIVE;
+ this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.IDLE;
}
this.doHumidifierUpdate.next();
}
@@ -706,117 +606,75 @@ export class Humidifier {
* Updates the status for each of the HomeKit Characteristics
*/
async updateHomeKitCharacteristics(): Promise {
- if (this.CurrentRelativeHumidity === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
+ if (this.HumidifierDehumidifier.CurrentRelativeHumidity === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentRelativeHumidity: ${this.HumidifierDehumidifier.CurrentRelativeHumidity}`);
} else {
- this.humidifierService.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity);
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` + ` updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`,
- );
- this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidity;
+ this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity,
+ this.HumidifierDehumidifier.CurrentRelativeHumidity);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` CurrentRelativeHumidity: ${this.HumidifierDehumidifier.CurrentRelativeHumidity}`);
+ this.accessory.context.CurrentRelativeHumidity = this.HumidifierDehumidifier.CurrentRelativeHumidity;
}
if (this.OpenAPI) {
- if (this.WaterLevel === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} WaterLevel: ${this.WaterLevel}`);
+ if (this.HumidifierDehumidifier.WaterLevel === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} WaterLevel: ${this.HumidifierDehumidifier.WaterLevel}`);
} else {
- this.humidifierService.updateCharacteristic(this.hap.Characteristic.WaterLevel, this.WaterLevel);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic WaterLevel: ${this.WaterLevel}`);
- this.accessory.context.WaterLevel = this.WaterLevel;
+ this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.WaterLevel, this.HumidifierDehumidifier.WaterLevel);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` WaterLevel: ${this.HumidifierDehumidifier.WaterLevel}`);
+ this.accessory.context.WaterLevel = this.HumidifierDehumidifier.WaterLevel;
}
}
- if (this.CurrentHumidifierDehumidifierState === undefined) {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` +
- ` CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`,
- );
+ if (this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentHumidifierDehumidifierState: ${this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState}`);
} else {
- this.humidifierService.updateCharacteristic(
- this.hap.Characteristic.CurrentHumidifierDehumidifierState,
- this.CurrentHumidifierDehumidifierState,
- );
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` +
- ` updateCharacteristic CurrentHumidifierDehumidifierState: ${this.CurrentHumidifierDehumidifierState}`,
- );
- this.accessory.context.CurrentHumidifierDehumidifierState = this.CurrentHumidifierDehumidifierState;
- }
- if (this.TargetHumidifierDehumidifierState === undefined) {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` + ` TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}`,
- );
+ this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentHumidifierDehumidifierState,
+ this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` CurrentHumidifierDehumidifierState: ${this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState}`);
+ this.accessory.context.CurrentHumidifierDehumidifierState = this.HumidifierDehumidifier.CurrentHumidifierDehumidifierState;
+ }
+ if (this.HumidifierDehumidifier.TargetHumidifierDehumidifierState === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` TargetHumidifierDehumidifierState: ${this.HumidifierDehumidifier.TargetHumidifierDehumidifierState}`);
} else {
- this.humidifierService.updateCharacteristic(
- this.hap.Characteristic.TargetHumidifierDehumidifierState,
- this.TargetHumidifierDehumidifierState,
- );
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` +
- ` updateCharacteristic TargetHumidifierDehumidifierState: ${this.TargetHumidifierDehumidifierState}`,
- );
- this.accessory.context.TargetHumidifierDehumidifierState = this.TargetHumidifierDehumidifierState;
- }
- if (this.Active === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.Active}`);
+ this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.TargetHumidifierDehumidifierState,
+ this.HumidifierDehumidifier.TargetHumidifierDehumidifierState);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` TargetHumidifierDehumidifierState: ${this.HumidifierDehumidifier.TargetHumidifierDehumidifierState}`);
+ this.accessory.context.TargetHumidifierDehumidifierState = this.HumidifierDehumidifier.TargetHumidifierDehumidifierState;
+ }
+ if (this.HumidifierDehumidifier.Active === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Active: ${this.HumidifierDehumidifier.Active}`);
} else {
- this.humidifierService.updateCharacteristic(this.hap.Characteristic.Active, this.Active);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.Active}`);
- this.accessory.context.Active = this.Active;
+ this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.Active, this.HumidifierDehumidifier.Active);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic Active: ${this.HumidifierDehumidifier.Active}`);
+ this.accessory.context.Active = this.HumidifierDehumidifier.Active;
}
- if (this.RelativeHumidityHumidifierThreshold === undefined) {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` +
- ` RelativeHumidityHumidifierThreshold: ${this.RelativeHumidityHumidifierThreshold}`,
- );
+ if (this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` RelativeHumidityHumidifierThreshold: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}`);
} else {
- this.humidifierService.updateCharacteristic(
- this.hap.Characteristic.RelativeHumidityHumidifierThreshold,
- this.RelativeHumidityHumidifierThreshold,
- );
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` +
- ` updateCharacteristic RelativeHumidityHumidifierThreshold: ${this.RelativeHumidityHumidifierThreshold}`,
- );
- this.accessory.context.RelativeHumidityHumidifierThreshold = this.RelativeHumidityHumidifierThreshold;
+ this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.RelativeHumidityHumidifierThreshold,
+ this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` RelativeHumidityHumidifierThreshold: ${this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold}`);
+ this.accessory.context.RelativeHumidityHumidifierThreshold = this.HumidifierDehumidifier.RelativeHumidityHumidifierThreshold;
}
if (!this.device.humidifier?.hide_temperature && !this.BLE) {
- if (this.CurrentTemperature === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`);
+ if (this.TemperatureSensor!.CurrentTemperature === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`);
} else {
- this.temperatureservice?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.CurrentTemperature);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`);
- this.accessory.context.CurrentTemperature = this.CurrentTemperature;
+ this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor!.CurrentTemperature);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`);
+ this.accessory.context.CurrentTemperature = this.TemperatureSensor!.CurrentTemperature;
}
}
}
- async stopScanning(switchbot: any) {
- switchbot.stopScan();
- if (this.connected) {
- await this.BLEparseStatus();
- await this.updateHomeKitCharacteristics();
- } else {
- await this.BLERefreshConnection(switchbot);
- }
- }
-
- async getCustomBLEAddress(switchbot: any) {
- if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
- (async () => {
- // Start to monitor advertisement packets
- await switchbot.startScan({
- model: 'e',
- });
- // Set an event handler
- switchbot.onadvertisement = (ad: any) => {
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
- };
- await sleep(10000);
- // Stop to monitor
- switchbot.stopScan();
- })();
- }
- }
-
async BLEPushConnection() {
if (this.platform.config.credentials?.token && this.device.connectionType === 'BLE/OpenAPI') {
this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} Using OpenAPI Connection to Push Changes`);
@@ -833,283 +691,28 @@ export class Humidifier {
}
}
- minStep(): number {
- if (this.device.humidifier?.set_minStep) {
- this.set_minStep = this.device.humidifier?.set_minStep;
- } else {
- this.set_minStep = 1;
- }
- return this.set_minStep;
- }
-
- async scan(device: device & devicesConfig): Promise {
- if (device.scanDuration) {
- this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
- }
- } else {
- this.scanDuration = this.accessory.context.scanDuration = 1;
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
- }
- }
- }
-
- async statusCode(statusCode: number): Promise {
- switch (statusCode) {
- case 151:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
- break;
- case 152:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
- break;
- case 160:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
- break;
- case 161:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
- this.offlineOff();
- break;
- case 171:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` +
- `Hub: ${this.device.hubDeviceId}`,
- );
- this.offlineOff();
- break;
- case 190:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
- ` Or command format is invalid, statusCode: ${statusCode}`,
- );
- break;
- case 100:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
- break;
- case 200:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
- break;
- case 400:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. `
- + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`);
- break;
- case 401:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, `
- + `but the request has not been authenticated, statusCode: ${statusCode}`);
- break;
- case 403:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not `
- + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`);
- break;
- case 404:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, `
- + `statusCode: ${statusCode}`);
- break;
- case 406:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via `
- + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`);
- break;
- case 415:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType `
- + `header that is not supported by the server, statusCode: ${statusCode}`);
- break;
- case 422:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, The client has made a valid request, `
- + `but the server cannot process it. This is often used for APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
- break;
- case 429:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of `
- + `requests allowed for a given time window, statusCode: ${statusCode}`);
- break;
- case 500:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings `
- + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`);
- break;
- default:
- this.infoLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` +
- `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`,
- );
- }
- }
-
async offlineOff(): Promise {
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} offline: ${this.device.offline}`);
if (this.device.offline) {
- await this.deviceContext();
- if (this.CurrentTemperature === undefined) {
- this.CurrentTemperature = 0;
- }
- await this.updateHomeKitCharacteristics();
+ this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentHumidifierDehumidifierState,
+ this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE);
+ this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.TargetHumidifierDehumidifierState,
+ this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER);
+ this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.Active, this.hap.Characteristic.Active.INACTIVE);
}
}
async apiError(e: any): Promise {
- this.humidifierService.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e);
+ this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e);
if (!this.BLE) {
- this.humidifierService.updateCharacteristic(this.hap.Characteristic.WaterLevel, e);
+ this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.WaterLevel, e);
}
- this.humidifierService.updateCharacteristic(this.hap.Characteristic.CurrentHumidifierDehumidifierState, e);
- this.humidifierService.updateCharacteristic(this.hap.Characteristic.TargetHumidifierDehumidifierState, e);
- this.humidifierService.updateCharacteristic(this.hap.Characteristic.Active, e);
- this.humidifierService.updateCharacteristic(this.hap.Characteristic.RelativeHumidityHumidifierThreshold, e);
+ this.HumidifierDehumidifier.Service.updateCharacteristic(this.hap.Characteristic.CurrentHumidifierDehumidifierState, e);
+ 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.BLE) {
- this.temperatureservice?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e);
- }
- }
-
- async deviceContext() {
- if (this.Active === undefined) {
- this.Active = this.hap.Characteristic.Active.ACTIVE;
- } else {
- this.Active = this.accessory.context.Active;
- }
- if (this.CurrentTemperature === undefined) {
- this.CurrentTemperature = 30;
- } else {
- this.CurrentTemperature = this.accessory.context.CurrentTemperature;
- }
- if (this.CurrentRelativeHumidity === undefined) {
- this.CurrentRelativeHumidity = 0;
- } else {
- this.CurrentRelativeHumidity = this.accessory.context.CurrentRelativeHumidity;
- }
- if (this.TargetHumidifierDehumidifierState === undefined) {
- this.TargetHumidifierDehumidifierState = this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER;
- } else if (this.accessory.context.TargetHumidifierDehumidifierState === undefined) {
- this.TargetHumidifierDehumidifierState = this.hap.Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER;
- } else {
- this.TargetHumidifierDehumidifierState = this.accessory.context.TargetHumidifierDehumidifierState;
- }
- if (this.CurrentHumidifierDehumidifierState === undefined) {
- this.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE;
- } else if (this.accessory.context.CurrentHumidifierDehumidifierState === undefined) {
- this.CurrentHumidifierDehumidifierState = this.hap.Characteristic.CurrentHumidifierDehumidifierState.INACTIVE;
- } else {
- this.CurrentHumidifierDehumidifierState = this.accessory.context.CurrentHumidifierDehumidifierState;
- }
- if (this.RelativeHumidityHumidifierThreshold === undefined) {
- this.RelativeHumidityHumidifierThreshold = 0;
- } else {
- this.RelativeHumidityHumidifierThreshold = this.accessory.context.RelativeHumidityHumidifierThreshold;
- }
- if (this.WaterLevel === undefined) {
- this.WaterLevel = 0;
- } else {
- this.WaterLevel = this.accessory.context.WaterLevel;
- }
- if (this.FirmwareRevision === undefined) {
- this.FirmwareRevision = this.platform.version;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
+ this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e);
}
}
-
- async refreshRate(device: device & devicesConfig): Promise {
- if (device.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
- } else if (this.platform.config.options!.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
- }
- }
-
- async deviceConfig(device: device & devicesConfig): Promise {
- let config = {};
- if (device.humidifier) {
- config = device.humidifier;
- }
- if (device.connectionType !== undefined) {
- config['connectionType'] = device.connectionType;
- }
- if (device.external !== undefined) {
- config['external'] = device.external;
- }
- if (device.logging !== undefined) {
- config['logging'] = device.logging;
- }
- if (device.refreshRate !== undefined) {
- config['refreshRate'] = device.refreshRate;
- }
- if (device.scanDuration !== undefined) {
- config['scanDuration'] = device.scanDuration;
- }
- if (device.offline !== undefined) {
- config['offline'] = device.offline;
- }
- if (device.webhook !== undefined) {
- config['webhook'] = device.webhook;
- }
- if (Object.entries(config).length !== 0) {
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
- }
- }
-
- async deviceLogs(device: device & devicesConfig): Promise {
- if (this.platform.debugMode) {
- this.deviceLogging = this.accessory.context.logging = 'debugMode';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
- } else if (device.logging) {
- this.deviceLogging = this.accessory.context.logging = device.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
- } else if (this.platform.config.options?.logging) {
- this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
- } else {
- this.deviceLogging = this.accessory.context.logging = 'standard';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
- }
- }
-
- /**
- * Logging for Device
- */
- async infoLog(...log: any[]): Promise {
- if (this.enablingDeviceLogging()) {
- this.platform.log.info(String(...log));
- }
- }
-
- async warnLog(...log: any[]): Promise {
- if (this.enablingDeviceLogging()) {
- this.platform.log.warn(String(...log));
- }
- }
-
- async debugWarnLog(...log: any[]): Promise {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.warn('[DEBUG]', String(...log));
- }
- }
- }
-
- async errorLog(...log: any[]): Promise {
- if (this.enablingDeviceLogging()) {
- this.platform.log.error(String(...log));
- }
- }
-
- async debugErrorLog(...log: any[]): Promise {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.error('[DEBUG]', String(...log));
- }
- }
- }
-
- async debugLog(...log: any[]): Promise {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging === 'debug') {
- this.platform.log.info('[DEBUG]', String(...log));
- } else {
- this.platform.log.debug(String(...log));
- }
- }
- }
-
- enablingDeviceLogging(): boolean {
- return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard';
- }
}
diff --git a/src/device/iosensor.ts b/src/device/iosensor.ts
index b987a701..3daaccfe 100644
--- a/src/device/iosensor.ts
+++ b/src/device/iosensor.ts
@@ -1,123 +1,102 @@
-import { hostname } from 'os';
-import { request } from 'undici';
-import { sleep } from '../utils.js';
-import { MqttClient } from 'mqtt';
-import asyncmqtt from 'async-mqtt';
-import { interval, Subject } from 'rxjs';
-import { skipWhile } from 'rxjs/operators';
-import { SwitchBotPlatform } from '../platform.js';
-import { Service, PlatformAccessory, Units, CharacteristicValue, API, Logging, HAP } from 'homebridge';
-import { device, devicesConfig, serviceData, temperature, deviceStatus, Devices, SwitchBotPlatformConfig } from '../settings.js';
+/* Copyright(C) 2021-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+ *
+ * iosensor.ts: @switchbot/homebridge-switchbot.
+ */
+import { Units } from 'homebridge';
+import { deviceBase } from './device.js';
+import { Devices } from '../settings.js';
+import { convertUnits } from '../utils.js';
+import { Subject, interval, skipWhile } from 'rxjs';
+
+import type { SwitchBotPlatform } from '../platform.js';
+import type { CharacteristicValue, PlatformAccessory, Service } from 'homebridge';
+import type { device, devicesConfig, serviceData, deviceStatus } from '../settings.js';
/**
* Platform Accessory
* An instance of this class is created for each accessory your platform registers
* Each accessory may expose multiple services of different service types.
*/
-export class IOSensor {
- public readonly api: API;
- public readonly log: Logging;
- public readonly config!: SwitchBotPlatformConfig;
- protected readonly hap: HAP;
+export class IOSensor extends deviceBase {
// Services
- batteryService: Service;
- humidityservice?: Service;
- temperatureservice?: Service;
-
- // Characteristic Values
- BatteryLevel!: CharacteristicValue;
- StatusLowBattery!: CharacteristicValue;
- FirmwareRevision!: CharacteristicValue;
- CurrentTemperature?: CharacteristicValue;
- CurrentRelativeHumidity!: CharacteristicValue;
-
- // OpenAPI Status
- OpenAPI_BatteryLevel: deviceStatus['battery'];
- OpenAPI_FirmwareRevision: deviceStatus['version'];
- OpenAPI_CurrentTemperature: deviceStatus['temperature'];
- OpenAPI_CurrentRelativeHumidity: deviceStatus['humidity'];
-
- // BLE Status
- BLE_Celsius!: temperature['c'];
- BLE_Fahrenheit!: temperature['f'];
- BLE_BatteryLevel!: serviceData['battery'];
- BLE_CurrentTemperature!: serviceData['temperature'];
- BLE_CurrentRelativeHumidity!: serviceData['humidity'];
-
- // BLE Others
- BLE_IsConnected?: boolean;
-
- //MQTT stuff
- mqttClient: MqttClient | null = null;
-
- // EVE history service handler
- historyService?: any;
-
- // Config
- scanDuration!: number;
- deviceLogging!: string;
- deviceRefreshRate!: number;
+ private Battery: {
+ Name: CharacteristicValue;
+ Service: Service;
+ BatteryLevel: CharacteristicValue;
+ StatusLowBattery: CharacteristicValue;
+ };
+
+ private HumiditySensor?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ CurrentRelativeHumidity: CharacteristicValue;
+ };
+
+ private TemperatureSensor?: {
+ Name: CharacteristicValue;
+ Service: Service;
+ CurrentTemperature: CharacteristicValue;
+ };
// Updates
ioSensorUpdateInProgress!: boolean;
doIOSensorUpdate: Subject;
- // Connection
- private readonly OpenAPI: boolean;
- private readonly BLE: boolean;
-
constructor(
- private readonly platform: SwitchBotPlatform,
- private accessory: PlatformAccessory,
- public device: device & devicesConfig,
+ readonly platform: SwitchBotPlatform,
+ accessory: PlatformAccessory,
+ device: device & devicesConfig,
) {
- this.api = this.platform.api;
- this.log = this.platform.log;
- this.config = this.platform.config;
- this.hap = this.api.hap;
- // Connection
- this.BLE = this.device.connectionType === 'BLE' || this.device.connectionType === 'BLE/OpenAPI';
- this.OpenAPI = this.device.connectionType === 'OpenAPI' || this.device.connectionType === 'BLE/OpenAPI';
- // default placeholders
- this.deviceLogs(device);
- this.scan(device);
- this.refreshRate(device);
- this.deviceContext();
- this.setupHistoryService(device);
- this.setupMqtt(device);
- this.deviceConfig(device);
-
+ super(platform, accessory, device);
// this is subject we use to track when we need to POST changes to the SwitchBot API
this.doIOSensorUpdate = new Subject();
this.ioSensorUpdateInProgress = false;
- // Retrieve initial values and updateHomekit
- this.refreshStatus();
+ // Initialize Battery Service
+ accessory.context.Battery = accessory.context.Battery ?? {};
+ this.Battery = {
+ Name: accessory.context.Battery.Name ?? `${accessory.displayName} Battery`,
+ Service: accessory.getService(this.hap.Service.Battery) ?? accessory.addService(this.hap.Service.Battery) as Service,
+ BatteryLevel: accessory.context.BatteryLevel ?? 100,
+ StatusLowBattery: accessory.context.StatusLowBattery ?? this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL,
+ };
+ accessory.context.Battery = this.Battery as object;
+
+ // Initialize Battery Characteristics
+ this.Battery.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.Battery.Name)
+ .setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE)
+ .getCharacteristic(this.hap.Characteristic.BatteryLevel)
+ .onGet(() => {
+ return this.Battery.BatteryLevel;
+ });
- // set accessory information
- accessory
- .getService(this.hap.Service.AccessoryInformation)!
- .setCharacteristic(this.hap.Characteristic.Manufacturer, 'SwitchBot')
- .setCharacteristic(this.hap.Characteristic.Model, 'WoIOSensor')
- .setCharacteristic(this.hap.Characteristic.SerialNumber, device.deviceId)
- .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
-
- // Temperature Sensor Service
- if (device.meter?.hide_temperature) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`);
- this.temperatureservice = this.accessory.getService(this.hap.Service.TemperatureSensor);
- accessory.removeService(this.temperatureservice!);
- } else if (!this.temperatureservice) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Temperature Sensor Service`);
- const temperatureservice = `${accessory.displayName} Temperature Sensor`;
- (this.temperatureservice = this.accessory.getService(this.hap.Service.TemperatureSensor)
- || this.accessory.addService(this.hap.Service.TemperatureSensor)), temperatureservice;
-
- this.temperatureservice.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Temperature Sensor`);
- if (!this.temperatureservice.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.temperatureservice.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Temperature Sensor`);
+ this.Battery.Service
+ .getCharacteristic(this.hap.Characteristic.StatusLowBattery)
+ .onGet(() => {
+ return this.Battery.StatusLowBattery;
+ });
+ accessory.context.BatteryName = this.Battery.Name;
+
+ // InitializeTemperature Sensor Service
+ if (device.iosensor?.hide_temperature) {
+ if (this.TemperatureSensor) {
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Temperature Sensor Service`);
+ this.TemperatureSensor.Service = this.accessory.getService(this.hap.Service.TemperatureSensor) as Service;
+ accessory.removeService(this.TemperatureSensor.Service);
}
- this.temperatureservice
+ } else {
+ accessory.context.TemperatureSensor = accessory.context.TemperatureSensor ?? {};
+ this.TemperatureSensor = {
+ Name: accessory.context.TemperatureSensor.Name ?? `${accessory.displayName} Temperature Sensor`,
+ Service: accessory.getService(this.hap.Service.TemperatureSensor) ?? this.accessory.addService(this.hap.Service.TemperatureSensor) as Service,
+ CurrentTemperature: accessory.context.CurrentTemperature ?? 30,
+ };
+ accessory.context.TemperatureSensor = this.TemperatureSensor as object;
+
+ // Initialize Temperature Sensor Characteristics
+ this.TemperatureSensor.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.TemperatureSensor.Name)
.getCharacteristic(this.hap.Characteristic.CurrentTemperature)
.setProps({
unit: Units['CELSIUS'],
@@ -127,49 +106,40 @@ export class IOSensor {
minStep: 0.1,
})
.onGet(() => {
- return this.CurrentTemperature!;
+ return this.TemperatureSensor!.CurrentTemperature;
});
- } else {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Temperature Sensor Service Not Added`);
}
- // Humidity Sensor Service
- if (device.meter?.hide_humidity) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`);
- this.humidityservice = this.accessory.getService(this.hap.Service.HumiditySensor);
- accessory.removeService(this.humidityservice!);
- } else if (!this.humidityservice) {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Add Humidity Sensor Service`);
- const humidityservice = `${accessory.displayName} Humidity Sensor`;
- (this.humidityservice = this.accessory.getService(this.hap.Service.HumiditySensor)
- || this.accessory.addService(this.hap.Service.HumiditySensor)), humidityservice;
-
- this.humidityservice.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Humidity Sensor`);
- if (!this.humidityservice.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.humidityservice.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Humidity Sensor`);
+ // Initialize Humidity Sensor Service
+ if (device.iosensor?.hide_humidity) {
+ if (this.HumiditySensor) {
+ this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Removing Humidity Sensor Service`);
+ this.HumiditySensor.Service = this.accessory.getService(this.hap.Service.HumiditySensor) as Service;
+ accessory.removeService(this.HumiditySensor.Service);
}
- this.humidityservice
+ } else {
+ accessory.context.HumiditySensor = accessory.context.HumiditySensor ?? {};
+ this.HumiditySensor = {
+ Name: accessory.context.HumiditySensor.Name ?? `${accessory.displayName} Humidity Sensor`,
+ Service: accessory.getService(this.hap.Service.HumiditySensor) ?? this.accessory.addService(this.hap.Service.HumiditySensor) as Service,
+ CurrentRelativeHumidity: accessory.context.CurrentRelativeHumidity ?? 50,
+ };
+ accessory.context.HumiditySensor = this.HumiditySensor as object;
+
+ // Initialize Humidity Sensor Characteristics
+ this.HumiditySensor.Service
+ .setCharacteristic(this.hap.Characteristic.Name, this.HumiditySensor.Name)
.getCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity)
.setProps({
minStep: 0.1,
})
.onGet(() => {
- return this.CurrentRelativeHumidity;
+ return this.HumiditySensor!.CurrentRelativeHumidity;
});
- } else {
- this.debugLog(`${this.device.deviceType}: ${accessory.displayName} Humidity Sensor Service Not Added`);
}
- // Battery Service
- const batteryService = `${accessory.displayName} Battery`;
- (this.batteryService = this.accessory.getService(this.hap.Service.Battery)
- || accessory.addService(this.hap.Service.Battery)), batteryService;
-
- this.batteryService.setCharacteristic(this.hap.Characteristic.Name, `${accessory.displayName} Battery`);
- if (!this.batteryService.testCharacteristic(this.hap.Characteristic.ConfiguredName)) {
- this.batteryService.addCharacteristic(this.hap.Characteristic.ConfiguredName, `${accessory.displayName} Battery`);
- }
- this.batteryService.setCharacteristic(this.hap.Characteristic.ChargingState, this.hap.Characteristic.ChargingState.NOT_CHARGEABLE);
+ // Retrieve initial values and updateHomekit
+ this.refreshStatus();
// Retrieve initial values and updateHomekit
this.updateHomeKitCharacteristics();
@@ -182,113 +152,77 @@ export class IOSensor {
});
//regisiter webhook event handler
- if (this.device.webhook) {
- this.infoLog(`${this.device.deviceType}: ${this.accessory.displayName} is listening webhook.`);
- this.platform.webhookEventHandler[this.device.deviceId] = async (context) => {
- try {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
- if (context.scale === 'CELSIUS') {
- const { temperature, humidity } = context;
- const { CurrentTemperature, CurrentRelativeHumidity } = this;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ` +
- '(temperature, humidity) = ' +
- `Webhook:(${temperature}, ${humidity}), ` +
- `current:(${CurrentTemperature}, ${CurrentRelativeHumidity})`);
- this.CurrentRelativeHumidity = humidity;
- this.CurrentTemperature = temperature;
- this.updateHomeKitCharacteristics();
- }
- } catch (e: any) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
- + `failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
- }
- };
- }
- }
-
- /**
- * Parse the device status from the SwitchBot api
- */
- async parseStatus(): Promise {
- if (!this.device.enableCloudService && this.OpenAPI) {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} parseStatus enableCloudService: ${this.device.enableCloudService}`);
- } else if (this.BLE) {
- await this.BLEparseStatus();
- } else if (this.OpenAPI && this.platform.config.credentials?.token) {
- await this.openAPIparseStatus();
- } else {
- await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` + ` ${this.device.connectionType}, parseStatus will not happen.`,
- );
- }
+ this.registerWebhook(accessory, device);
}
- async BLEparseStatus(): Promise {
+ async BLEparseStatus(serviceData: serviceData): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BLEparseStatus`);
// Battery
- this.BatteryLevel = Number(this.BLE_BatteryLevel);
- if (this.BatteryLevel < 15) {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
+ this.Battery.BatteryLevel = Number(serviceData.battery);
+ if (this.Battery.BatteryLevel < 10) {
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
} else {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
}
- this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`);
+ this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}, StatusLowBattery: ${this.Battery.StatusLowBattery}`);
// Humidity
- if (!this.device.meter?.hide_humidity) {
- this.CurrentRelativeHumidity = this.BLE_CurrentRelativeHumidity!;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`);
+ if (!this.device.iosensor?.hide_humidity) {
+ this.HumiditySensor!.CurrentRelativeHumidity = serviceData.humidity!;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.HumiditySensor!.CurrentRelativeHumidity}%`);
}
// Current Temperature
- if (!this.device.meter?.hide_temperature) {
- this.BLE_Celsius < 0 ? 0 : this.BLE_Celsius > 100 ? 100 : this.BLE_Celsius;
- this.CurrentTemperature = this.BLE_Celsius;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`);
+ if (!this.device.iosensor?.hide_temperature) {
+ serviceData.temperature!.c < 0 ? 0 : serviceData.temperature!.c > 100 ? 100 : serviceData.temperature!.c;
+ this.TemperatureSensor!.CurrentTemperature = serviceData.temperature!.c;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`);
}
}
- async openAPIparseStatus(): Promise {
+ async openAPIparseStatus(deviceStatus: deviceStatus): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIparseStatus`);
- // Battery
- this.BatteryLevel = Number(this.OpenAPI_BatteryLevel);
- if (this.BatteryLevel < 15) {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
+
+ // BatteryLevel
+ this.Battery.BatteryLevel = Number(deviceStatus.body.battery);
+ if (this.Battery.BatteryLevel < 10) {
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
} else {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
+ this.Battery.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
}
- this.debugLog(`${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}, StatusLowBattery: ${this.StatusLowBattery}`);
+ if (Number.isNaN(this.Battery.BatteryLevel)) {
+ this.Battery.BatteryLevel = 100;
+ }
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel},`
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
// Current Relative Humidity
- if (!this.device.meter?.hide_humidity) {
- this.CurrentRelativeHumidity = this.OpenAPI_CurrentRelativeHumidity!;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.CurrentRelativeHumidity}%`);
+ if (!this.device.iosensor?.hide_humidity) {
+ this.HumiditySensor!.CurrentRelativeHumidity = deviceStatus.body.humidity!;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Humidity: ${this.HumiditySensor!.CurrentRelativeHumidity}%`);
}
// Current Temperature
- if (!this.device.meter?.hide_temperature) {
- this.CurrentTemperature = this.OpenAPI_CurrentTemperature!;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.CurrentTemperature}°c`);
+ if (!this.device.iosensor?.hide_temperature) {
+ this.TemperatureSensor!.CurrentTemperature = deviceStatus.body.temperature!;
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Temperature: ${this.TemperatureSensor!.CurrentTemperature}°c`);
+ }
+
+ // Firmware Version
+ const version = deviceStatus.body.version?.toString();
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Firmware Version: ${version?.replace(/^V|-.*$/g, '')}`);
+ if (deviceStatus.body.version) {
+ const deviceVersion = version?.replace(/^V|-.*$/g, '') ?? '0.0.0';
+ this.accessory
+ .getService(this.hap.Service.AccessoryInformation)!
+ .setCharacteristic(this.hap.Characteristic.HardwareRevision, deviceVersion)
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, deviceVersion)
+ .getCharacteristic(this.hap.Characteristic.FirmwareRevision)
+ .updateValue(deviceVersion);
+ this.accessory.context.deviceVersion = deviceVersion;
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceVersion: ${this.accessory.context.deviceVersion}`);
}
-
- // BatteryLevel
- this.BatteryLevel = Number(this.OpenAPI_BatteryLevel);
- if (this.BatteryLevel < 10) {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW;
- } else {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
- }
- if (Number.isNaN(this.BatteryLevel)) {
- this.BatteryLevel = 100;
- }
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel},`
- + ` StatusLowBattery: ${this.StatusLowBattery}`);
-
- // FirmwareRevision
- this.FirmwareRevision = this.OpenAPI_FirmwareRevision!;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
}
/**
@@ -303,10 +237,8 @@ export class IOSensor {
await this.openAPIRefreshStatus();
} else {
await this.offlineOff();
- this.debugWarnLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Connection Type:` +
- ` ${this.device.connectionType}, refreshStatus will not happen.`,
- );
+ this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Connection Type:`
+ + ` ${this.device.connectionType}, refreshStatus will not happen.`);
}
}
@@ -323,107 +255,43 @@ export class IOSensor {
// Start to monitor advertisement packets
(async () => {
// Start to monitor advertisement packets
- await switchbot.startScan({
- model: 'w',
- id: this.device.bleMac,
- });
+ await switchbot.startScan({ model: this.device.bleModel, id: this.device.bleMac });
// Set an event handler
switchbot.onadvertisement = (ad: any) => {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.serviceData.model}`);
- if (this.device.bleMac === ad.address && ad.serviceData.model === 'w') {
+ if (this.device.bleMac === ad.address && ad.model === this.device.bleModel) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} ${JSON.stringify(ad, null, ' ')}`);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} address: ${ad.address}, model: ${ad.model}`);
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.BLE_CurrentTemperature = ad.serviceData.temperature;
- this.BLE_Celsius = ad.serviceData.temperature!.c;
- this.BLE_Fahrenheit = ad.serviceData.temperature!.f;
- this.BLE_BatteryLevel = ad.serviceData.battery;
} else {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
}
};
- // Wait 1 seconds
+ // Wait 10 seconds
await switchbot.wait(this.scanDuration * 1000);
// Stop to monitor
await switchbot.stopScan();
// Update HomeKit
- await this.BLEparseStatus();
+ await this.BLEparseStatus(switchbot.onadvertisement.serviceData);
await this.updateHomeKitCharacteristics();
})();
- /*if (switchbot !== false) {
- switchbot
- .startScan({
- model: 'w',
- id: this.device.bleMac,
- })
- .then(async () => {
- // Set an event handler
- switchbot.onadvertisement = async (ad: ad) => {
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Config BLE Address: ${this.device.bleMac},` +
- ` BLE Address Found: ${ad.address}`,
- );
- if (ad.serviceData.humidity! > 0) {
- // reject unreliable data
- this.BLE_CurrentRelativeHumidity = ad.serviceData.humidity;
- }
- this.BLE_CurrentTemperature = ad.serviceData.temperature;
- this.BLE_Celsius = ad.serviceData.temperature!.c;
- this.BLE_Fahrenheit = ad.serviceData.temperature!.f;
- this.BLE_BatteryLevel = ad.serviceData.battery;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} serviceData: ${JSON.stringify(ad.serviceData)}`);
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName} model: ${ad.serviceData.model}, modelName: ${ad.serviceData.modelName}, ` +
- `temperature: ${JSON.stringify(ad.serviceData.temperature?.c)}, humidity: ${ad.serviceData.humidity}, ` +
- `battery: ${ad.serviceData.battery}`,
- );
-
- if (ad.serviceData) {
- this.BLE_IsConnected = true;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
- await this.stopScanning(switchbot);
- } else {
- this.BLE_IsConnected = false;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} connected: ${this.BLE_IsConnected}`);
- }
- };
- // Wait
- return await sleep(this.scanDuration * 1000);
- })
- .then(async () => {
- // Stop to monitor
- await this.stopScanning(switchbot);
- })
- .catch(async (e: any) => {
- this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed BLERefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
- await this.BLERefreshConnection(switchbot);
- });
- } else {
+ if (switchbot === undefined) {
await this.BLERefreshConnection(switchbot);
- }*/
+ }
}
async openAPIRefreshStatus(): Promise {
this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} openAPIRefreshStatus`);
try {
- const { body, statusCode } = await request(`${Devices}/${this.device.deviceId}/status`, {
- headers: this.platform.generateHeaders(),
- });
+ const { body, statusCode } = await this.platform.retryRequest(this.deviceMaxRetries, this.deviceDelayBetweenRetries,
+ `${Devices}/${this.device.deviceId}/status`, { headers: this.platform.generateHeaders() });
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} statusCode: ${statusCode}`);
const deviceStatus: any = await body.json();
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus: ${JSON.stringify(deviceStatus)}`);
this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} deviceStatus statusCode: ${deviceStatus.statusCode}`);
if ((statusCode === 200 || statusCode === 100) && (deviceStatus.statusCode === 200 || deviceStatus.statusCode === 100)) {
- this.debugErrorLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ this.debugSuccessLog(`${this.device.deviceType}: ${this.accessory.displayName} `
+ `statusCode: ${statusCode} & deviceStatus StatusCode: ${deviceStatus.statusCode}`);
- this.OpenAPI_CurrentRelativeHumidity = deviceStatus.body.humidity!;
- this.OpenAPI_CurrentTemperature = deviceStatus.body.temperature!;
- this.OpenAPI_BatteryLevel = deviceStatus.body.battery;
- this.OpenAPI_FirmwareRevision = deviceStatus.body.version;
- this.openAPIparseStatus();
+ this.openAPIparseStatus(deviceStatus);
this.updateHomeKitCharacteristics();
} else {
this.statusCode(statusCode);
@@ -431,10 +299,40 @@ export class IOSensor {
}
} catch (e: any) {
this.apiError(e);
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}` +
- ` Connection, Error Message: ${JSON.stringify(e.message)}`,
- );
+ this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} failed openAPIRefreshStatus with ${this.device.connectionType}`
+ + ` Connection, Error Message: ${JSON.stringify(e.message)}`);
+ }
+ }
+
+ async registerWebhook(accessory: PlatformAccessory, device: device & devicesConfig) {
+ if (device.webhook) {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is listening webhook.`);
+ this.platform.webhookEventHandler[device.deviceId] = async (context) => {
+ try {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} received Webhook: ${JSON.stringify(context)}`);
+ const { temperature, humidity } = context;
+ const { CurrentTemperature } = this.TemperatureSensor || { CurrentTemperature: undefined };
+ const { CurrentRelativeHumidity } = this.HumiditySensor || { CurrentRelativeHumidity: undefined };
+ if (context.scale !== 'CELCIUS' && device.iosensor?.convertUnitTo === undefined) {
+ this.warnLog(`${device.deviceType}: ${accessory.displayName} received a non-CELCIUS Webhook scale: `
+ + `${context.scale}, Use the *convertUnitsTo* config under Indoor/Outdoor Sensor settings, if displaying incorrectly in HomeKit.`);
+ }
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} (scale, temperature, humidity) = Webhook:(${context.scale},`
+ + ` ${convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo)}, ${humidity}), current:(${CurrentTemperature}, `
+ + `${CurrentRelativeHumidity})`);
+ if (device.iosensor?.hide_humidity) {
+ this.HumiditySensor!.CurrentRelativeHumidity = humidity;
+ }
+ if (device.iosensor?.hide_temperature) {
+ this.TemperatureSensor!.CurrentTemperature = convertUnits(temperature, context.scale, device.iosensor?.convertUnitTo);
+ }
+ this.updateHomeKitCharacteristics();
+ } catch (e: any) {
+ this.errorLog(`${device.deviceType}: ${accessory.displayName} failed to handle webhook. Received: ${JSON.stringify(context)} Error: ${e}`);
+ }
+ };
+ } else {
+ this.debugLog(`${device.deviceType}: ${accessory.displayName} is not listening webhook.`);
}
}
@@ -444,63 +342,73 @@ export class IOSensor {
async updateHomeKitCharacteristics(): Promise {
const mqttmessage: string[] = [];
const entry = { time: Math.round(new Date().valueOf() / 1000) };
- if (!this.device.meter?.hide_humidity) {
- if (this.CurrentRelativeHumidity === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`);
+
+ // CurrentRelativeHumidity
+ if (!this.device.iosensor?.hide_humidity) {
+ if (this.HumiditySensor!.CurrentRelativeHumidity === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName}`
+ + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`);
} else {
- this.accessory.context.CurrentRelativeHumidity = this.CurrentRelativeHumidity;
- this.humidityservice?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, this.CurrentRelativeHumidity);
- this.debugLog(
- `${this.device.deviceType}: ${this.accessory.displayName}` +
- ` updateCharacteristic CurrentRelativeHumidity: ${this.CurrentRelativeHumidity}`,
- );
+ this.accessory.context.CurrentRelativeHumidity = this.HumiditySensor!.CurrentRelativeHumidity;
+ this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity,
+ this.HumiditySensor!.CurrentRelativeHumidity);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` CurrentRelativeHumidity: ${this.HumiditySensor!.CurrentRelativeHumidity}`);
if (this.device.mqttURL) {
- mqttmessage.push(`"humidity": ${this.CurrentRelativeHumidity}`);
+ mqttmessage.push(`"humidity": ${this.HumiditySensor!.CurrentRelativeHumidity}`);
}
if (this.device.history) {
- entry['humidity'] = this.CurrentRelativeHumidity;
+ entry['humidity'] = this.HumiditySensor!.CurrentRelativeHumidity;
}
}
}
- if (!this.device.meter?.hide_temperature) {
- if (this.CurrentTemperature === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.CurrentTemperature}`);
+
+ // CurrentTemperature
+ if (!this.device.iosensor?.hide_temperature) {
+ if (this.TemperatureSensor!.CurrentTemperature === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`);
} else {
+ this.accessory.context.CurrentTemperature = this.TemperatureSensor!.CurrentTemperature;
+ this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.TemperatureSensor!.CurrentTemperature);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` CurrentTemperature: ${this.TemperatureSensor!.CurrentTemperature}`);
if (this.device.mqttURL) {
- mqttmessage.push(`"temperature": ${this.CurrentTemperature}`);
+ mqttmessage.push(`"temperature": ${this.TemperatureSensor!.CurrentTemperature}`);
}
if (this.device.history) {
- entry['temp'] = this.CurrentTemperature;
+ entry['temp'] = this.TemperatureSensor!.CurrentTemperature;
}
- this.accessory.context.CurrentTemperature = this.CurrentTemperature;
- this.temperatureservice?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.CurrentTemperature);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic CurrentTemperature: ${this.CurrentTemperature}`);
}
}
- if (this.BatteryLevel === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.BatteryLevel}`);
+ if (this.Battery.BatteryLevel === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} BatteryLevel: ${this.Battery.BatteryLevel}`);
} else {
+ this.accessory.context.BatteryLevel = this.Battery.BatteryLevel;
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.Battery.BatteryLevel);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.Battery.BatteryLevel}`);
if (this.device.mqttURL) {
- mqttmessage.push(`"battery": ${this.BatteryLevel}`);
+ mqttmessage.push(`"battery": ${this.Battery.BatteryLevel}`);
}
- this.accessory.context.BatteryLevel = this.BatteryLevel;
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, this.BatteryLevel);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic BatteryLevel: ${this.BatteryLevel}`);
}
- if (this.StatusLowBattery === undefined) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.StatusLowBattery}`);
+ if (this.Battery.StatusLowBattery === undefined) {
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} StatusLowBattery: ${this.Battery.StatusLowBattery}`);
} else {
+ this.accessory.context.StatusLowBattery = this.Battery.StatusLowBattery;
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.Battery.StatusLowBattery);
+ this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic`
+ + ` StatusLowBattery: ${this.Battery.StatusLowBattery}`);
if (this.device.mqttURL) {
- mqttmessage.push(`"lowBattery": ${this.StatusLowBattery}`);
+ mqttmessage.push(`"lowBattery": ${this.Battery.StatusLowBattery}`);
}
- this.accessory.context.StatusLowBattery = this.StatusLowBattery;
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, this.StatusLowBattery);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} updateCharacteristic StatusLowBattery: ${this.StatusLowBattery}`);
}
+
+ // MQTT Publish
if (this.device.mqttURL) {
this.mqttPublish(`{${mqttmessage.join(',')}}`);
}
- if (Number(this.CurrentRelativeHumidity) > 0) {
+
+ // History Service
+ if (!this.device.iosensor?.hide_humidity && (Number(this.HumiditySensor!.CurrentRelativeHumidity) > 0)) {
// reject unreliable data
if (this.device.history) {
this.historyService?.addEntry(entry);
@@ -508,84 +416,6 @@ export class IOSensor {
}
}
- /*
- * Publish MQTT message for topics of
- * 'homebridge-switchbot/meter/xx:xx:xx:xx:xx:xx'
- */
- mqttPublish(message: any) {
- const mac = this.device.deviceId
- ?.toLowerCase()
- .match(/[\s\S]{1,2}/g)
- ?.join(':');
- const options = this.device.mqttPubOptions || {};
- this.mqttClient?.publish(`homebridge-switchbot/meter/${mac}`, `${message}`, options);
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT message: ${message} options:${JSON.stringify(options)}`);
- }
-
- /*
- * Setup MQTT hadler if URL is specified.
- */
- async setupMqtt(device: device & devicesConfig): Promise {
- if (device.mqttURL) {
- try {
- const { connectAsync } = asyncmqtt;
- this.mqttClient = await connectAsync(device.mqttURL, device.mqttOptions || {});
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} MQTT connection has been established successfully.`);
- this.mqttClient.on('error', (e: Error) => {
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to publish MQTT messages. ${e}`);
- });
- } catch (e) {
- this.mqttClient = null;
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Failed to establish MQTT connection. ${e}`);
- }
- }
- }
-
- /*
- * Setup EVE history graph feature if enabled.
- */
- async setupHistoryService(device: device & devicesConfig): Promise {
- const mac = this.device
- .deviceId!.match(/.{1,2}/g)!
- .join(':')
- .toLowerCase();
- this.historyService = device.history
- ? new this.platform.fakegatoAPI('room', this.accessory, {
- log: this.platform.log,
- storage: 'fs',
- filename: `${hostname().split('.')[0]}_${mac}_persist.json`,
- })
- : null;
- }
-
- async stopScanning(switchbot: any) {
- switchbot.stopScan();
- if (this.BLE_IsConnected) {
- await this.BLEparseStatus();
- await this.updateHomeKitCharacteristics();
- } else {
- await this.BLERefreshConnection(switchbot);
- }
- }
-
- async getCustomBLEAddress(switchbot: any) {
- if (this.device.customBLEaddress && this.deviceLogging.includes('debug')) {
- (async () => {
- // Start to monitor advertisement packets
- await switchbot.startScan({
- model: 'i',
- });
- // Set an event handler
- switchbot.onadvertisement = (ad: any) => {
- this.warnLog(`${this.device.deviceType}: ${this.accessory.displayName} ad: ${JSON.stringify(ad, null, ' ')}`);
- };
- await sleep(10000);
- // Stop to monitor
- switchbot.stopScan();
- })();
- }
- }
-
async BLERefreshConnection(switchbot: any): Promise {
this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} wasn't able to establish BLE Connection, node-switchbot:`
+ ` ${JSON.stringify(switchbot)}`);
@@ -595,249 +425,26 @@ export class IOSensor {
}
}
- async scan(device: device & devicesConfig): Promise {
- if (device.scanDuration) {
- this.scanDuration = this.accessory.context.scanDuration = device.scanDuration;
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config scanDuration: ${this.scanDuration}`);
- }
- } else {
- this.scanDuration = this.accessory.context.scanDuration = 1;
- if (this.BLE) {
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Default scanDuration: ${this.scanDuration}`);
- }
- }
- }
-
- async statusCode(statusCode: number): Promise {
- switch (statusCode) {
- case 151:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command not supported by this deviceType, statusCode: ${statusCode}`);
- break;
- case 152:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device not found, statusCode: ${statusCode}`);
- break;
- case 160:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Command is not supported, statusCode: ${statusCode}`);
- break;
- case 161:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Device is offline, statusCode: ${statusCode}`);
- this.offlineOff();
- break;
- case 171:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Hub Device is offline, statusCode: ${statusCode}. ` +
- `Hub: ${this.device.hubDeviceId}`,
- );
- this.offlineOff();
- break;
- case 190:
- this.errorLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Device internal error due to device states not synchronized with server,` +
- ` Or command format is invalid, statusCode: ${statusCode}`,
- );
- break;
- case 100:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Command successfully sent, statusCode: ${statusCode}`);
- break;
- case 200:
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Request successful, statusCode: ${statusCode}`);
- break;
- case 400:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Bad Request, The client has issued an invalid request. `
- + `This is commonly used to specify validation errors in a request payload, statusCode: ${statusCode}`);
- break;
- case 401:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unauthorized, Authorization for the API is required, `
- + `but the request has not been authenticated, statusCode: ${statusCode}`);
- break;
- case 403:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Forbidden, The request has been authenticated but does not `
- + `have appropriate permissions, or a requested resource is not found, statusCode: ${statusCode}`);
- break;
- case 404:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Found, Specifies the requested path does not exist, `
- + `statusCode: ${statusCode}`);
- break;
- case 406:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Not Acceptable, The client has requested a MIME type via `
- + `the Accept header for a value not supported by the server, statusCode: ${statusCode}`);
- break;
- case 415:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unsupported Media Type, The client has defined a contentType `
- + `header that is not supported by the server, statusCode: ${statusCode}`);
- break;
- case 422:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Unprocessable Entity, The client has made a valid request, `
- + `but the server cannot process it. This is often used for APIs for which certain limits have been exceeded, statusCode: ${statusCode}`);
- break;
- case 429:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Too Many Requests, The client has exceeded the number of `
- + `requests allowed for a given time window, statusCode: ${statusCode}`);
- break;
- case 500:
- this.errorLog(`${this.device.deviceType}: ${this.accessory.displayName} Internal Server Error, An unexpected error on the SmartThings `
- + `servers has occurred. These errors should be rare, statusCode: ${statusCode}`);
- break;
- default:
- this.infoLog(
- `${this.device.deviceType}: ${this.accessory.displayName} Unknown statusCode: ` +
- `${statusCode}, Submit Bugs Here: ' + 'https://tinyurl.com/SwitchBotBug`,
- );
- }
- }
-
async offlineOff(): Promise {
if (this.device.offline) {
- await this.deviceContext();
- await this.updateHomeKitCharacteristics();
- }
- }
-
- async apiError(e: any): Promise {
- if (!this.device.meter?.hide_humidity) {
- this.humidityservice?.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e);
- }
- if (!this.device.meter?.hide_temperature) {
- this.temperatureservice?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e);
- }
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e);
- this.batteryService?.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e);
- }
-
- async deviceContext() {
- if (this.CurrentRelativeHumidity === undefined) {
- this.CurrentRelativeHumidity = 0;
- } else {
- this.CurrentRelativeHumidity = this.accessory.context.CurrentRelativeHumidity;
- }
- if (this.CurrentTemperature === undefined) {
- this.CurrentTemperature = 0;
- } else {
- this.CurrentTemperature = this.accessory.context.CurrentTemperature;
- }
- if (this.BatteryLevel === undefined) {
- this.BatteryLevel = 100;
- } else {
- this.BatteryLevel = this.accessory.context.BatteryLevel;
- }
- if (this.StatusLowBattery === undefined) {
- this.StatusLowBattery = this.hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL;
- this.accessory.context.StatusLowBattery = this.StatusLowBattery;
- } else {
- this.StatusLowBattery = this.accessory.context.StatusLowBattery;
- }
- if (this.FirmwareRevision === undefined) {
- this.FirmwareRevision = this.platform.version;
- this.accessory.context.FirmwareRevision = this.FirmwareRevision;
- }
- }
-
- async refreshRate(device: device & devicesConfig): Promise {
- if (device.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
- } else if (this.platform.config.options!.refreshRate) {
- this.deviceRefreshRate = this.accessory.context.refreshRate = this.platform.config.options!.refreshRate;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
- }
- }
-
- async deviceConfig(device: device & devicesConfig): Promise {
- let config = {};
- if (device.meter) {
- config = device.meter;
- }
- if (device.connectionType !== undefined) {
- config['connectionType'] = device.connectionType;
- }
- if (device.external !== undefined) {
- config['external'] = device.external;
- }
- if (device.mqttURL !== undefined) {
- config['mqttURL'] = device.mqttURL;
- }
- if (device.logging !== undefined) {
- config['logging'] = device.logging;
- }
- if (device.refreshRate !== undefined) {
- config['refreshRate'] = device.refreshRate;
- }
- if (device.scanDuration !== undefined) {
- config['scanDuration'] = device.scanDuration;
- }
- if (device.webhook !== undefined) {
- config['webhook'] = device.webhook;
- }
- if (Object.entries(config).length !== 0) {
- this.debugWarnLog(`${this.device.deviceType}: ${this.accessory.displayName} Config: ${JSON.stringify(config)}`);
- }
- }
-
- async deviceLogs(device: device & devicesConfig): Promise {
- if (this.platform.debugMode) {
- this.deviceLogging = this.accessory.context.logging = 'debugMode';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
- } else if (device.logging) {
- this.deviceLogging = this.accessory.context.logging = device.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
- } else if (this.platform.config.options?.logging) {
- this.deviceLogging = this.accessory.context.logging = this.platform.config.options?.logging;
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
- } else {
- this.deviceLogging = this.accessory.context.logging = 'standard';
- this.debugLog(`${this.device.deviceType}: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
- }
- }
-
- /**
- * Logging for Device
- */
- infoLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.info(String(...log));
- }
- }
-
- warnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.warn(String(...log));
- }
- }
-
- debugWarnLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.warn('[DEBUG]', String(...log));
+ if (!this.device.iosensor?.hide_humidity) {
+ this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, 50);
}
- }
- }
-
- errorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- this.platform.log.error(String(...log));
- }
- }
-
- debugErrorLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging?.includes('debug')) {
- this.platform.log.error('[DEBUG]', String(...log));
+ if (!this.device.iosensor?.hide_temperature) {
+ this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, 30);
}
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, 100);
}
}
- debugLog(...log: any[]): void {
- if (this.enablingDeviceLogging()) {
- if (this.deviceLogging === 'debug') {
- this.platform.log.info('[DEBUG]', String(...log));
- } else {
- this.platform.log.debug(String(...log));
- }
+ async apiError(e: any): Promise {
+ if (!this.device.iosensor?.hide_humidity) {
+ this.HumiditySensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentRelativeHumidity, e);
}
- }
-
- enablingDeviceLogging(): boolean {
- return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard';
+ if (!this.device.iosensor?.hide_temperature) {
+ this.TemperatureSensor!.Service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e);
+ }
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.BatteryLevel, e);
+ this.Battery.Service.updateCharacteristic(this.hap.Characteristic.StatusLowBattery, e);
}
}
diff --git a/src/device/lightstrip.ts b/src/device/lightstrip.ts
index 01dae57b..e4709146 100644
--- a/src/device/lightstrip.ts
+++ b/src/device/lightstrip.ts
@@ -1,141 +1,106 @@
import { request } from 'undici';
-import { sleep } from '../utils.js';
-import { interval, Subject } from 'rxjs';
-import { SwitchBotPlatform } from '../platform.js';
-import { debounceTime, skipWhile, take, tap } from 'rxjs/operators';
-import {
- Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap, API, Logging, HAP,
-} from 'homebridge';
-import { device, devicesConfig, hs2rgb, rgb2hs, deviceStatus, serviceData, m2hs, Devices, SwitchBotPlatformConfig } from '../settings.js';
+import { deviceBase } from './device.js';
+import { Devices } from '../settings.js';
+import { hs2rgb, rgb2hs, m2hs } from '../utils.js';
+import { interval, skipWhile, Subject } from 'rxjs';
+import { debounceTime, take, tap } from 'rxjs/operators';
+
+import type { SwitchBotPlatform } from '../platform.js';
+import type { device, devicesConfig, deviceStatus, serviceData } from '../settings.js';
+import type { Service, PlatformAccessory, CharacteristicValue, ControllerConstructor, Controller, ControllerServiceMap } from 'homebridge';
/**
* Platform Accessory
* An instance of this class is created for each accessory your platform registers
* Each accessory may expose multiple services of different service types.
*/
-export class StripLight {
- public readonly api: API;
- public readonly log: Logging;
- public readonly config!: SwitchBotPlatformConfig;
- protected readonly hap: HAP;
+export class StripLight extends deviceBase {
// Services
- lightBulbService!: Service;
-
- // Characteristic Values
- On!: CharacteristicValue;
- Hue!: CharacteristicValue;
- Saturation!: CharacteristicValue;
- Brightness!: CharacteristicValue;
- FirmwareRevision!: CharacteristicValue;
- ColorTemperature?: CharacteristicValue;
-
- // OpenAPI Status
- OpenAPI_On: deviceStatus['power'];
- OpenAPI_RGB: deviceStatus['color'];
- OpenAPI_Brightness: deviceStatus['brightness'];
- OpenAPI_FirmwareRevision: deviceStatus['version'];
-
- // BLE Status
- BLE_On: serviceData['state'];
- BLE_Brightness: serviceData['brightness'];
-
- // BLE Others
- BLE_IsConnected?: boolean;
-
- // Config
- set_minStep?: number;
- scanDuration!: number;
- deviceLogging!: string;
- deviceRefreshRate!: number;
- adaptiveLightingShift?: number;
+ private LightBulb: {
+ Name: CharacteristicValue;
+ Service: Service;
+ On: CharacteristicValue;
+ Hue: CharacteristicValue;
+ Saturation: CharacteristicValue;
+ Brightness: CharacteristicValue;
+ ColorTemperature?: CharacteristicValue;
+ };
// Adaptive Lighting
AdaptiveLightingController?: ControllerConstructor | Controller