├── .gitattributes ├── logo.png ├── .gitmodules ├── src ├── index.js ├── types.js ├── accessories │ ├── WyzePlug.js │ ├── WyzeAccessory.js │ ├── WyzeSwitch.js │ ├── WyzeMotionSensor.js │ ├── WyzeLeakSensor.js │ ├── WyzeHMS.js │ ├── WyzeContactSensor.js │ ├── WyzeLight.js │ ├── WyzeTemperatureHumidity.js │ ├── WyzeLock.js │ ├── WyzeMeshLight.js │ ├── WyzeThermostat.js │ └── WyzeCamera.js ├── enums.js └── WyzeSmartHome.js ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature-request.md │ ├── support-request.md │ └── bug-report.md ├── dependabot.yml └── workflows │ ├── greetings.yml │ └── codeql.yml ├── .gitignore ├── license.txt ├── configSample.json ├── package.json ├── README.md ├── CHANGELOG.md ├── config.schema.json └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jfarmer08/homebridge-wyze-smart-home/HEAD/logo.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/wyze-api"] 2 | path = src/wyze-api 3 | url = https://github.com/jfarmer08/wyze-api 4 | branch = main 5 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const types = require('./types') 2 | 3 | // Boot the plugin 4 | module.exports = function (homebridge) { 5 | types.update(homebridge) 6 | 7 | require('./WyzeSmartHome').register() 8 | } 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | # blank_issues_enabled: false 2 | # contact_links: 3 | # - name: Homebridge Discord Community 4 | # url: https://discord.gg/kqNCe2D 5 | # about: Ask your questions in the #YOUR_CHANNEL_HERE channel -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ~/ 2 | .DS_Store 3 | .uix-dashboard.json 4 | .uix-secrets 5 | auth.json 6 | node_modules/.bin/ 7 | node_modules/ 8 | persist/ 9 | package-lock.json 10 | config.json 11 | backups/ 12 | *.bak 13 | testing/ 14 | accessories/uiAccessoriesLayout.json 15 | bun.lockb 16 | accessories/*cachedAccessories* 17 | homebridge.log 18 | -------------------------------------------------------------------------------- /src/types.js: -------------------------------------------------------------------------------- 1 | const types = {} 2 | 3 | module.exports = types 4 | 5 | module.exports.update = function (homebridge) { 6 | types.homebridge = homebridge 7 | types.Accessory = homebridge.platformAccessory 8 | types.Service = homebridge.hap.Service 9 | types.Characteristic = homebridge.hap.Characteristic 10 | types.UUIDGen = homebridge.hap.uuid 11 | types.HapStatusError = homebridge.hap.HapStatusError 12 | types.HAPStatus = homebridge.hap.HAPStatus 13 | } 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: [pull_request_target, issues] 4 | 5 | jobs: 6 | greeting: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | steps: 12 | - uses: actions/first-interaction@v1 13 | with: 14 | repo-token: ${{ secrets.GITHUB_TOKEN }} 15 | issue-message: "Message that will be displayed on users' first issue" 16 | pr-message: "Message that will be displayed on users' first pull request" 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe:** 11 | 12 | 13 | **Describe the solution you'd like:** 14 | 15 | 16 | **Describe alternatives you've considered:** 17 | 18 | 19 | **Additional context:** 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/support-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Support Request 3 | about: Need help? 4 | title: '' 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | **Describe Your Problem:** 13 | 14 | 15 | **Logs:** 16 | 17 | ``` 18 | Show the Homebridge logs here, remove any sensitive information. 19 | ``` 20 | 21 | **Plugin Config:** 22 | 23 | ```json 24 | Show your Homebridge config.json here, remove any sensitive information. 25 | ``` 26 | 27 | **Screenshots:** 28 | 29 | 30 | **Environment:** 31 | 32 | * **Plugin Version**: 33 | * **Homebridge Version**: 34 | * **Node.js Version**: 35 | * **NPM Version**: 36 | * **Operating System**: 37 | 38 | 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug, question 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | **Describe The Bug:** 13 | 14 | 15 | **To Reproduce:** 16 | 17 | 18 | **Expected behavior:** 19 | 20 | 21 | **Logs:** 22 | 23 | ``` 24 | Show the Homebridge logs here, remove any sensitive information. 25 | ``` 26 | 27 | **Plugin Config:** 28 | 29 | ```json 30 | Show your Homebridge config.json here, remove any sensitive information. 31 | ``` 32 | 33 | **Screenshots:** 34 | 35 | 36 | **Environment:** 37 | 38 | * **Plugin Version**: 39 | * **Homebridge Version**: 40 | * **Node.js Version**: 41 | * **NPM Version**: 42 | * **Operating System**: 43 | 44 | 45 | -------------------------------------------------------------------------------- /configSample.json: -------------------------------------------------------------------------------- 1 | { 2 | "bridge": { 3 | "name": "Home Test", 4 | "username": "0E:D1:4B:32:A8:65", 5 | "port": 56666, 6 | "pin": "935-15-250", 7 | "advertiser": "bonjour-hap", 8 | "bind": [ 9 | "en0" 10 | ] 11 | }, 12 | "accessories": [], 13 | "platforms": [ 14 | { 15 | "name": "Config", 16 | "port": 8581, 17 | "auth": "form", 18 | "theme": "auto", 19 | "tempUnits": "f", 20 | "lang": "auto", 21 | "platform": "config" 22 | }, 23 | { 24 | "name": "Wyze", 25 | "username": "email", 26 | "password": "password", 27 | "keyId": "keyID", 28 | "apiKey": "apiKey", 29 | "refreshInterval": 20000, 30 | "entryExitDelay": 30, 31 | "apiLogEnabled": true, 32 | "pluginLoggingEnabled": true, 33 | "showAdvancedOptions": true, 34 | "excludeMacAddress": false, 35 | "excludedeviceType": true, 36 | "filterDeviceTypeList": [ 37 | "OutdoorPlug", 38 | "Plug", 39 | "Light", 40 | "MeshLight", 41 | "LightStrip", 42 | "ContactSensor", 43 | "MotionSensor", 44 | "Lock", 45 | "TemperatureHumidity", 46 | "LeakSensor", 47 | "S1Gateway", 48 | "Common" 49 | ], 50 | "platform": "WyzeSmartHome" 51 | } 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "homebridge-wyze-smart-home", 3 | "version": "0.5.47", 4 | "description": "Wyze Smart Home plugin for Homebridge", 5 | "license": "MIT", 6 | "main": "src/index.js", 7 | "scripts": { 8 | "lint": "eslint src/**.js --max-warnings=0", 9 | }, 10 | "keywords": [ 11 | "homebridge-plugin", 12 | "Wyze", 13 | "Hoobs" 14 | ], 15 | "homepage": "http://github.com/jfarmer08/homebridge-wyze-smart-home", 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/jfarmer08/homebridge-wyze-smart-home.git" 19 | }, 20 | "bugs": { 21 | "url": "http://github.com/jfarmer08/homebridge-wyze-smart-home/issues" 22 | }, 23 | "engines": { 24 | "homebridge": "^1.6.0 || ^2.0.0-beta.0", 25 | "node": "^18.20.4 || ^20.15.1" 26 | }, 27 | "dependencies": { 28 | "axios": "^1.5.0", 29 | "base64-js": "1.5.1", 30 | "colorsys": "^1.0.22", 31 | "crypto-js": "4.2.0", 32 | "homebridge-config-ui-x": "^4.56.4", 33 | "inherits": "2.0.4", 34 | "md5": "^2.2.1", 35 | "moment": "2.29.4", 36 | "querystring": "0.2.1", 37 | "urllib": "3.18.0", 38 | "utf8": "^3.0.0", 39 | "uuid": "8.3.2", 40 | "uuid-by-string": "4.0.0", 41 | "wyze-api": "1.1.7", 42 | "aws-sdk": "2.1679.0" 43 | }, 44 | "devDependencies": { 45 | "@typescript-eslint/eslint-plugin": "^5.27.1", 46 | "@typescript-eslint/parser": "^5.27.1", 47 | "eslint": "^8.17.0", 48 | "eslint-config-standard": "^17.0.0", 49 | "homebridge": "^2.0.0-beta.0" 50 | }, 51 | "funding": [ 52 | { 53 | "type": "paypal", 54 | "url": "https://paypal.me/AllenFarmer" 55 | }, 56 | { 57 | "type": "venmo", 58 | "url": "https://venmo.com/u/Allen-Farmer" 59 | }, 60 | { 61 | "type": "cashApp", 62 | "url": "https://cash.app/$Jfamer08" 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /src/accessories/WyzePlug.js: -------------------------------------------------------------------------------- 1 | const { Service, Characteristic } = require("../types"); 2 | const WyzeAccessory = require("./WyzeAccessory"); 3 | 4 | const noResponse = new Error("No Response"); 5 | noResponse.toString = () => { 6 | return noResponse.message; 7 | }; 8 | 9 | module.exports = class WyzePlug extends WyzeAccessory { 10 | constructor(plugin, homeKitAccessory) { 11 | super(plugin, homeKitAccessory); 12 | 13 | this.getOnCharacteristic().on("set", this.set.bind(this)); 14 | } 15 | 16 | updateCharacteristics(device) { 17 | if (this.plugin.config.pluginLoggingEnabled) 18 | this.plugin.log( 19 | `[Plug] Updating status of "${this.display_name} (${this.mac})"` 20 | ); 21 | if (device.conn_state === 0) { 22 | this.getOnCharacteristic().updateValue(noResponse); 23 | } else { 24 | this.getOnCharacteristic().updateValue(device.device_params.switch_state); 25 | } 26 | } 27 | 28 | getOutletService() { 29 | if (this.plugin.config.pluginLoggingEnabled) 30 | this.plugin.log( 31 | `[Plug] Retrieving previous service for "${this.display_name} (${this.mac})"` 32 | ); 33 | let service = this.homeKitAccessory.getService(Service.Outlet); 34 | 35 | if (!service) { 36 | if (this.plugin.config.pluginLoggingEnabled) 37 | this.plugin.log( 38 | `[Plug] Adding service for "${this.display_name} (${this.mac})"` 39 | ); 40 | service = this.homeKitAccessory.addService(Service.Outlet); 41 | } 42 | 43 | return service; 44 | } 45 | 46 | getOnCharacteristic() { 47 | if (this.plugin.config.pluginLoggingEnabled) 48 | this.plugin.log( 49 | `[Plug] Fetching status of "${this.display_name} (${this.mac})"` 50 | ); 51 | return this.getOutletService().getCharacteristic(Characteristic.On); 52 | } 53 | 54 | async set(value, callback) { 55 | if (this.plugin.config.pluginLoggingEnabled) 56 | this.plugin.log( 57 | `[Plug] Setting power for "${this.display_name} (${this.mac})" to ${value}` 58 | ); 59 | 60 | try { 61 | await this.plugin.client.plugPower( 62 | this.mac, 63 | this.product_model, 64 | value ? "1" : "0" 65 | ); 66 | callback(); 67 | } catch (e) { 68 | callback(e); 69 | } 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /src/accessories/WyzeAccessory.js: -------------------------------------------------------------------------------- 1 | const { Service, Characteristic } = require("../types"); 2 | 3 | // Responses from the Wyze API can lag a little after a new value is set 4 | const UPDATE_THROTTLE_MS = 1000; 5 | 6 | module.exports = class WyzeAccessory { 7 | constructor(plugin, homeKitAccessory) { 8 | this.updating = false; 9 | this.lastTimestamp = null; 10 | 11 | this.plugin = plugin; 12 | this.homeKitAccessory = homeKitAccessory; 13 | } 14 | 15 | // Default Prop 16 | get display_name() { 17 | return this.homeKitAccessory.displayName; 18 | } 19 | get mac() { 20 | return this.homeKitAccessory.context.mac; 21 | } 22 | get product_type() { 23 | return this.homeKitAccessory.context.product_type; 24 | } 25 | get product_model() { 26 | return this.homeKitAccessory.context.product_model; 27 | } 28 | 29 | /** Determines whether this accessory matches the given Wyze device */ 30 | matches(device) { 31 | return this.mac === device.mac; 32 | } 33 | 34 | async update(device, timestamp) { 35 | const productType = device.product_type; 36 | 37 | switch (productType) { 38 | default: 39 | this.homeKitAccessory.context = { 40 | mac: device.mac, 41 | product_type: device.product_type, 42 | product_model: device.product_model, 43 | nickname: device.nickname, 44 | }; 45 | break; 46 | } 47 | 48 | this.homeKitAccessory 49 | .getService(Service.AccessoryInformation) 50 | .updateCharacteristic(Characteristic.Name, device.nickname) 51 | .updateCharacteristic(Characteristic.Manufacturer, "Wyze") 52 | .updateCharacteristic(Characteristic.Model, device.product_model) 53 | .updateCharacteristic(Characteristic.SerialNumber, device.mac) 54 | .updateCharacteristic( 55 | Characteristic.FirmwareRevision, 56 | device.firmware_ver 57 | ); 58 | 59 | if (this.shouldUpdateCharacteristics(timestamp)) { 60 | this.updateCharacteristics(device); 61 | } 62 | } 63 | shouldUpdateCharacteristics(timestamp) { 64 | if (this.updating) { 65 | return false; 66 | } 67 | 68 | if ( 69 | this.lastTimestamp && 70 | timestamp <= this.lastTimestamp + UPDATE_THROTTLE_MS 71 | ) { 72 | return false; 73 | } 74 | 75 | return true; 76 | } 77 | 78 | updateCharacteristics(device) { 79 | // 80 | } 81 | 82 | sleep(ms) { 83 | return new Promise((resolve) => setTimeout(resolve, ms * 1000)); 84 | } 85 | }; 86 | -------------------------------------------------------------------------------- /src/enums.js: -------------------------------------------------------------------------------- 1 | const CameraModels = { 2 | WyzeCamv1Hd: "WYZEC1", 3 | WyzeCamV2: "WYZEC1-JZ", 4 | WyzeCamV3: "WYZE_CAKP2JFUS", 5 | WyzeCamV3Pro: "HL_CAM3P", 6 | WyzeCamV4: "HL_CAM4", 7 | WyzeCamFloodlight: "WYZE_CAKP2JFUS", 8 | WyzeCamPan: "WYZECP1_JEF", 9 | WyzeCamPanv2: "HL_PAN2", 10 | WyzeCamPanv3: "HL_PAN3", 11 | WyzeCamOutdoor: "WVOD1", 12 | WyzeCamOutdoor2: "HL_WCO2" 13 | } 14 | exports.CameraModels = CameraModels; 15 | 16 | const OutdoorPlugModels = { 17 | WLPPOSUB: "WLPPO-SUB" 18 | } 19 | exports.OutdoorPlugModels = OutdoorPlugModels 20 | 21 | const PlugModels = { WLPP1: "WLPP1", WLPP1CFH: "WLPP1CFH" } 22 | exports.PlugModels = PlugModels 23 | 24 | const LightModels = { BULB_WHITE: "WLPA19", BULB_WHITE_V2: "HL_HWB2" } 25 | exports.LightModels = LightModels 26 | 27 | const MeshLightModels = { MESH_BULB: "WLPA19C", HL_BR30C: "HL_BR30C", HL_A19C2: 'HL_A19C2' } 28 | exports.MeshLightModels = MeshLightModels 29 | 30 | const LightStripModels = { LIGHT_STRIP: "HL_LSL", LIGHT_STRIP_PRO: "HL_LSLP" } 31 | exports.LightStripModels = LightStripModels 32 | 33 | const ContactSensorModels = { "V1": "DWS2U", "V2": "DWS3U" } 34 | exports.ContactSensorModels = ContactSensorModels 35 | 36 | const MotionSensorModels = { V1: "PIR2U", V2: "PIR3U" } 37 | exports.MotionSensorModels = MotionSensorModels 38 | 39 | const LockModels = { YDLO1: "YD.LO1" } 40 | exports.LockModels = LockModels 41 | 42 | const TemperatureHumidityModels = { TH3U: "TH3U" } 43 | exports.TemperatureHumidityModels = TemperatureHumidityModels 44 | 45 | const LeakSensorModels = { WS3U: "WS3U" } 46 | exports.LeakSensorModels = LeakSensorModels 47 | 48 | const CommonModels = { "LightSwitch": "LD_SS1" } 49 | exports.CommonModels = CommonModels 50 | 51 | const S1GatewayModels = { 'GW3U': 'GW3U' } 52 | exports.S1GatewayModels = S1GatewayModels 53 | 54 | const ThermostatModels = { CO_EA1: "CO_EA1" } 55 | exports.ThermostatModels = ThermostatModels 56 | 57 | const ThermostatRoomSensor = { CO_TH1: "CO_TH1" } 58 | exports.ThermostatRoomSensor = ThermostatRoomSensor 59 | 60 | //"OutdoorPlugMain" : "WLPPO", "ChimeSensor" : "CHIME", "HeadPhones":"JA_HP","YDGW1":"YD.GW1", 61 | //"Scale_S":"WL_SC3","WL_SC2":"WL_SC2", "JA_RO2":"JA_RO2", "Sprinkler":"BS_WK1", "ThermostatRoomSensor":"CO_TH1", 62 | //"BLE_Lock":"YD_BT1","JA_SL10":"JA_SL10"} 63 | //WyzeCamPanPro: "HL_PANP", 64 | //WyzeCamOutdoov2: "HL_WCO2", 65 | //WyzeCamDoorbell: "WYZEDB3", 66 | //WyzeBatteryCamPro: "AN_RSCW", 67 | //WyzeCamDoorbellPro2: "AN_RDB1", 68 | //WyzeCamFloodLightPro: "LD_CFP", 69 | //WyzeCamDoorbellPro: "GW_BE1", 70 | //WyzeCamOG: "GW_GC1", 71 | //WyzeCamOGTelephoto3x: "GW_GC" 72 | //VACUUM = ['JA_RO2'] 73 | //WyzeScale = ['JA.SC', 'JA.SC2'] 74 | //SCALE_S = ['WL_SC2'] 75 | //SCALE_X = ['WL_SC22135'] 76 | //WATCH = ['RA.WP1', 'RY.WA1'] 77 | //Wrist = ['RY.HP1'] -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "main" ] 20 | schedule: 21 | - cron: '36 13 * * 1' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'Typescript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 53 | # queries: security-extended,security-and-quality 54 | 55 | 56 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 57 | # If this step fails, then you should remove it and run the build manually (see below) 58 | - name: Autobuild 59 | uses: github/codeql-action/autobuild@v2 60 | 61 | # ℹ️ Command-line programs to run using the OS shell. 62 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 63 | 64 | # If the Autobuild fails above, remove it and uncomment the following three lines. 65 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 66 | 67 | # - run: | 68 | # echo "Run, Build Application using script" 69 | # ./location_of_script_within_repo/buildscript.sh 70 | 71 | - name: Perform CodeQL Analysis 72 | uses: github/codeql-action/analyze@v2 73 | -------------------------------------------------------------------------------- /src/accessories/WyzeSwitch.js: -------------------------------------------------------------------------------- 1 | const { Service, Characteristic } = require("../types"); 2 | const WyzeAccessory = require("./WyzeAccessory"); 3 | //A stateless programable switch is button that resets after pressing (think push button). 4 | const SinglePressType = { 5 | CLASSIC: 1, // Classic Control 6 | IOT: 2, // Smart Control 7 | }; 8 | 9 | const noResponse = new Error("No Response"); 10 | noResponse.toString = () => { 11 | return noResponse.message; 12 | }; 13 | 14 | module.exports = class WyzeSwitch extends WyzeAccessory { 15 | constructor(plugin, homeKitAccessory) { 16 | super(plugin, homeKitAccessory); 17 | 18 | // create a new Switch service 19 | if (this.plugin.config.pluginLoggingEnabled) 20 | this.plugin.log( 21 | `[Switch] Retrieving previous service for "${this.display_name} (${this.mac})"` 22 | ); 23 | this.wallSwitch = this.homeKitAccessory.getService(Service.Switch); 24 | 25 | if (!this.wallSwitch) { 26 | if (this.plugin.config.pluginLoggingEnabled) 27 | this.plugin.log( 28 | `[Switch] Adding service for "${this.display_name} (${this.mac})"` 29 | ); 30 | this.wallSwitch = this.homeKitAccessory.addService(Service.Switch); 31 | } 32 | 33 | this.wallSwitch 34 | .getCharacteristic(Characteristic.On) 35 | .onGet(this.handleOnGetWallSwitch.bind(this)) 36 | .onSet(this.handleOnSetWallSwitch.bind(this)); 37 | } 38 | 39 | async updateCharacteristics(device) { 40 | if (device.conn_state === 0) { 41 | this.wallSwitch 42 | .getCharacteristic(Characteristic.On) 43 | .updateValue(noResponse); 44 | } else { 45 | if (this.plugin.config.pluginLoggingEnabled) 46 | this.plugin.log( 47 | `[Switch] Updating status of "${this.display_name} (${this.mac})"` 48 | ); 49 | const propertyList = await this.plugin.client.getIotProp(this.mac); 50 | for (const prop of Object.keys(propertyList.data.props)) { 51 | switch (prop) { 52 | case "iot_state": 53 | this.iot_state = propertyList.data.props[prop]; 54 | break; 55 | case "single_press_type": 56 | this.single_press_type = propertyList.data.props[prop]; 57 | break; 58 | case "double_press_type": 59 | this.double_press_type = propertyList.data.props[prop]; 60 | case "triple_press_type": 61 | this.triple_press_type = propertyList.data.props[prop]; 62 | break; 63 | case "long_press_type": 64 | this.long_press_type = propertyList.data.props[prop]; 65 | break; 66 | case "switch-power": 67 | this.wallSwitch 68 | .getCharacteristic(Characteristic.On) 69 | .updateValue(propertyList.data.props[prop]); 70 | this.switch_power = propertyList.data.props[prop]; 71 | break; 72 | case "switch-iot": 73 | this.switch_iot = propertyList.data.props[prop]; 74 | break; 75 | } 76 | } 77 | } 78 | } 79 | 80 | async handleOnGetWallSwitch() { 81 | if (this.plugin.config.pluginLoggingEnabled) 82 | this.plugin.log( 83 | `[Switch] Getting Current State of "${this.display_name} (${this.mac})" : "${this.switch_power}"` 84 | ); 85 | return this.switch_power; 86 | } 87 | 88 | async handleOnSetWallSwitch(value) { 89 | if (this.plugin.config.pluginLoggingEnabled) 90 | this.plugin.log( 91 | `[Switch] Target State Set "${this.display_name} (${this.mac})" : "${value}"` 92 | ); 93 | if (this.single_press_type == SinglePressType.IOT) { 94 | await this.plugin.client.wallSwitchIot( 95 | this.mac, 96 | this.product_model, 97 | value ? true : false 98 | ); 99 | } else { 100 | await this.plugin.client.wallSwitchPower( 101 | this.mac, 102 | this.product_model, 103 | value ? true : false 104 | ); 105 | } 106 | } 107 | }; 108 | -------------------------------------------------------------------------------- /src/accessories/WyzeMotionSensor.js: -------------------------------------------------------------------------------- 1 | const { Service, Characteristic } = require("../types"); 2 | const WyzeAccessory = require("./WyzeAccessory"); 3 | 4 | const noResponse = new Error("No Response"); 5 | noResponse.toString = () => { 6 | return noResponse.message; 7 | }; 8 | 9 | module.exports = class WyzeMotionSensor extends WyzeAccessory { 10 | constructor(plugin, homeKitAccessory) { 11 | super(plugin, homeKitAccessory); 12 | 13 | this.getOnCharacteristic(); 14 | this.getBatteryCharacteristic(); 15 | this.getIsBatteryLowCharacteristic(); 16 | } 17 | 18 | getSensorService() { 19 | if (this.plugin.config.pluginLoggingEnabled) 20 | this.plugin.log( 21 | `[MotionSensor] Retrieving previous service for "${this.display_name} (${this.mac})"` 22 | ); 23 | let service = this.homeKitAccessory.getService(Service.MotionSensor); 24 | 25 | if (!service) { 26 | if (this.plugin.config.pluginLoggingEnabled) 27 | this.plugin.log( 28 | `[MotionSensor] Adding service for "${this.display_name} (${this.mac})"` 29 | ); 30 | service = this.homeKitAccessory.addService(Service.MotionSensor); 31 | } 32 | 33 | return service; 34 | } 35 | 36 | getBatterySensorService() { 37 | if (this.plugin.config.pluginLoggingEnabled) 38 | this.plugin.log( 39 | `[MotionSensorBattery] Retrieving previous service for "${this.display_name} (${this.mac})"` 40 | ); 41 | let service = this.homeKitAccessory.getService(Service.Battery); 42 | 43 | if (!service) { 44 | if (this.plugin.config.pluginLoggingEnabled) 45 | this.plugin.log( 46 | `[MotionSensorBattery] Adding service for "${this.display_name} (${this.mac})"` 47 | ); 48 | service = this.homeKitAccessory.addService(Service.Battery); 49 | } 50 | 51 | return service; 52 | } 53 | 54 | getIsBatteryLowSensorService() { 55 | if (this.plugin.config.pluginLoggingEnabled) 56 | this.plugin.log( 57 | `[MotionSensorIsBatteryLow] Retrieving previous service for "${this.display_name} (${this.mac})"` 58 | ); 59 | let service = this.homeKitAccessory.getService(Service.Battery); 60 | 61 | if (!service) { 62 | if (this.plugin.config.pluginLoggingEnabled) 63 | this.plugin.log( 64 | `[MotionSensorIsBatteryLow] Adding service for "${this.display_name} (${this.mac})"` 65 | ); 66 | service = this.homeKitAccessory.addService(Service.Battery); 67 | } 68 | 69 | return service; 70 | } 71 | 72 | getOnCharacteristic() { 73 | if (this.plugin.config.pluginLoggingEnabled) 74 | this.plugin.log( 75 | `[MotionSensor] Fetching status of "${this.display_name} (${this.mac})"` 76 | ); 77 | return this.getSensorService().getCharacteristic( 78 | Characteristic.MotionDetected 79 | ); 80 | } 81 | 82 | getBatteryCharacteristic() { 83 | if (this.plugin.config.pluginLoggingEnabled) 84 | this.plugin.log( 85 | `[MotionSensorBattery] Fetching status of "${this.display_name} (${this.mac})"` 86 | ); 87 | return this.getBatterySensorService().getCharacteristic( 88 | Characteristic.BatteryLevel 89 | ); 90 | } 91 | 92 | getIsBatteryLowCharacteristic() { 93 | if (this.plugin.config.pluginLoggingEnabled) 94 | this.plugin.log( 95 | `[MotionSensorBattery] Fetching status of "${this.display_name} (${this.mac})"` 96 | ); 97 | return this.getIsBatteryLowSensorService().getCharacteristic( 98 | Characteristic.StatusLowBattery 99 | ); 100 | } 101 | 102 | updateCharacteristics(device) { 103 | if (this.plugin.config.pluginLoggingEnabled) 104 | this.plugin.log( 105 | `[MotionSensor] Updating status of "${this.display_name} (${this.mac})"` 106 | ); 107 | if (device.conn_state === 0) { 108 | this.getOnCharacteristic().updateValue(noResponse); 109 | } else { 110 | this.getOnCharacteristic().updateValue(device.device_params.motion_state); 111 | this.getBatteryCharacteristic().updateValue( 112 | this.plugin.client.checkBatteryVoltage(device.device_params.voltage) 113 | ); 114 | this.getIsBatteryLowCharacteristic().updateValue( 115 | this.plugin.client.checkLowBattery(device.device_params.voltage) 116 | ); 117 | } 118 | } 119 | }; 120 | -------------------------------------------------------------------------------- /src/accessories/WyzeLeakSensor.js: -------------------------------------------------------------------------------- 1 | const { Service, Characteristic } = require("../types"); 2 | const WyzeAccessory = require("./WyzeAccessory"); 3 | 4 | const noResponse = new Error("No Response"); 5 | noResponse.toString = () => { 6 | return noResponse.message; 7 | }; 8 | 9 | module.exports = class WyzeHumidity extends WyzeAccessory { 10 | constructor(plugin, homeKitAccessory) { 11 | super(plugin, homeKitAccessory); 12 | 13 | this.getOnCharacteristic(); 14 | this.getBatteryCharacteristic(); 15 | this.getIsBatteryLowCharacteristic(); 16 | } 17 | 18 | getSensorService() { 19 | if (this.plugin.config.pluginLoggingEnabled) 20 | this.plugin.log( 21 | `[LeakSensor] Retrieving previous service for "${this.display_name}"` 22 | ); 23 | let service = this.homeKitAccessory.getService(Service.LeakSensor); 24 | 25 | if (!service) { 26 | if (this.plugin.config.pluginLoggingEnabled) 27 | this.plugin.log( 28 | `[LeakSensor] Adding service for "${this.display_name}"` 29 | ); 30 | service = this.homeKitAccessory.addService(Service.LeakSensor); 31 | } 32 | 33 | return service; 34 | } 35 | 36 | getBatterySensorService() { 37 | if (this.plugin.config.pluginLoggingEnabled) 38 | this.plugin.log( 39 | `[LeakSensorBattery] Retrieving previous service for "${this.display_name}"` 40 | ); 41 | let service = this.homeKitAccessory.getService(Service.Battery); 42 | 43 | if (!service) { 44 | if (this.plugin.config.pluginLoggingEnabled) 45 | this.plugin.log( 46 | `[LeakSensorBattery] Adding service for "${this.display_name}"` 47 | ); 48 | service = this.homeKitAccessory.addService(Service.Battery); 49 | } 50 | 51 | return service; 52 | } 53 | 54 | getIsBatteryLowSensorService() { 55 | if (this.plugin.config.pluginLoggingEnabled) 56 | this.plugin.log( 57 | `[LeakSensorBatteryLow] Retrieving previous service for "${this.display_name}"` 58 | ); 59 | let service = this.homeKitAccessory.getService(Service.Battery); 60 | 61 | if (!service) { 62 | if (this.plugin.config.pluginLoggingEnabled) 63 | this.plugin.log( 64 | `[LeakSensorIsBatteryLow] Adding service for "${this.display_name}"` 65 | ); 66 | service = this.homeKitAccessory.addService(Service.Battery); 67 | } 68 | 69 | return service; 70 | } 71 | 72 | getOnCharacteristic() { 73 | if (this.plugin.config.pluginLoggingEnabled) 74 | this.plugin.log( 75 | `[LeakSensor] Fetching status of "${this.display_name}"` 76 | ); 77 | return this.getSensorService().getCharacteristic( 78 | Characteristic.LeakDetected 79 | ); 80 | } 81 | 82 | getBatteryCharacteristic() { 83 | if (this.plugin.config.pluginLoggingEnabled) 84 | this.plugin.log( 85 | `[LeakSensorBattery] Fetching status of "${this.display_name}"` 86 | ); 87 | return this.getBatterySensorService().getCharacteristic( 88 | Characteristic.BatteryLevel 89 | ); 90 | } 91 | 92 | getIsBatteryLowCharacteristic() { 93 | if (this.plugin.config.pluginLoggingEnabled) 94 | this.plugin.log( 95 | `[LeakSensorBattery] Fetching status of "${this.display_name}"` 96 | ); 97 | return this.getIsBatteryLowSensorService().getCharacteristic( 98 | Characteristic.StatusLowBattery 99 | ); 100 | } 101 | 102 | async updateCharacteristics(device) { 103 | if (device.conn_state === 0) { 104 | if (this.plugin.config.pluginLoggingEnabled) 105 | this.plugin.log( 106 | `[LeakSensor] Updating status ${this.mac} (${this.display_name}) to noResponse` 107 | ); 108 | this.getOnCharacteristic().updateValue(noResponse); 109 | } else { 110 | if (this.plugin.config.pluginLoggingEnabled) { 111 | this.plugin.log( 112 | `[LeakSensor] Updating status of ${this.mac} (${this.display_name})` 113 | ); 114 | } 115 | this.getOnCharacteristic().updateValue( 116 | this.plugin.client.getLeakSensorState( 117 | device.device_params.ws_detect_state 118 | ) 119 | ); 120 | this.getBatteryCharacteristic().updateValue( 121 | this.plugin.client.checkBatteryVoltage(device.device_params.voltage) 122 | ); 123 | this.getIsBatteryLowCharacteristic().updateValue( 124 | this.plugin.client.checkLowBattery(device.device_params.voltage) 125 | ); 126 | } 127 | } 128 | }; 129 | -------------------------------------------------------------------------------- /src/accessories/WyzeHMS.js: -------------------------------------------------------------------------------- 1 | const { Service, Characteristic } = require("../types"); 2 | const WyzeAccessory = require("./WyzeAccessory"); 3 | 4 | const noResponse = new Error("No Response"); 5 | noResponse.toString = () => { 6 | return noResponse.message; 7 | }; 8 | 9 | module.exports = class WyzeHMS extends WyzeAccessory { 10 | constructor(plugin, homeKitAccessory) { 11 | super(plugin, homeKitAccessory); 12 | 13 | // create a new Security System service 14 | if (this.plugin.config.pluginLoggingEnabled) 15 | this.plugin.log( 16 | `[HMS] Retrieving previous service for "${this.display_name}"` 17 | ); 18 | this.securityService = this.homeKitAccessory.getService( 19 | Service.SecuritySystem 20 | ); 21 | 22 | if (!this.securityService) { 23 | if (this.plugin.config.pluginLoggingEnabled) 24 | this.plugin.log(`[HMS] Adding service for "${this.display_name}"`); 25 | this.securityService = this.homeKitAccessory.addService( 26 | Service.SecuritySystem 27 | ); 28 | } 29 | 30 | this.securityService 31 | .getCharacteristic(Characteristic.SecuritySystemCurrentState) 32 | .onGet(this.handleSecuritySystemCurrentStateGet.bind(this)); 33 | 34 | this.securityService 35 | .getCharacteristic(Characteristic.SecuritySystemTargetState) 36 | .onGet(this.handleSecuritySystemTargetStateGet.bind(this)) 37 | .onSet(this.handleSecuritySystemTargetStateSet.bind(this)); 38 | } 39 | 40 | async updateCharacteristics(device) { 41 | if (device.conn_state === 0) { 42 | if (this.plugin.config.pluginLoggingEnabled) 43 | this.plugin.log( 44 | `[HMS] Updating status ${this.mac} (${this.display_name}) to noResponse` 45 | ); 46 | this.getCharacteristic(Characteristic.SecuritySystemCurrentState).updateValue(noResponse); 47 | } else { 48 | if (this.plugin.config.pluginLoggingEnabled) 49 | this.plugin.log( 50 | `[HMS] Updating Current State of "${this.display_name}"` 51 | ); 52 | await this.getHmsID(); 53 | const response = await this.plugin.client.monitoringProfileStateStatus( 54 | this.hmsId 55 | ); 56 | this.hmsStatus = response.message; 57 | this.securityService 58 | .getCharacteristic(Characteristic.SecuritySystemCurrentState) 59 | .updateValue(this.convertHmsStateToHomeKitState(this.hmsStatus)); 60 | } 61 | } 62 | 63 | async handleSecuritySystemCurrentStateGet() { 64 | if (this.plugin.config.pluginLoggingEnabled) 65 | this.plugin.log( 66 | `[HMS] Getting Current State of "${this.display_name}" : "${this.hmsStatus}"` 67 | ); 68 | if (this.hmsStatus === "undefined" || this.hmsStatus == null) { 69 | return 0; 70 | } else return this.convertHmsStateToHomeKitState(this.hmsStatus); 71 | } 72 | 73 | async handleSecuritySystemTargetStateGet() { 74 | if (this.plugin.config.pluginLoggingEnabled) 75 | this.plugin.log( 76 | `[HMS] Getting Target State of "${this.display_name}" : "${this.hmsStatus}"` 77 | ); 78 | if (this.hmsStatus === "undefined" || this.hmsStatus == null) { 79 | return 0; 80 | } else return this.convertHmsStateToHomeKitState(this.hmsStatus); 81 | } 82 | 83 | async handleSecuritySystemTargetStateSet(value) { 84 | if (this.plugin.config.pluginLoggingEnabled) 85 | this.plugin.log( 86 | `[HMS] Target State Set "${ 87 | this.display_name 88 | }" : "${this.convertHomeKitStateToHmsState(value)}"` 89 | ); 90 | await this.plugin.client.setHMSState( 91 | this.hmsId, 92 | this.convertHomeKitStateToHmsState(value) 93 | ); 94 | } 95 | 96 | convertHmsStateToHomeKitState(hmsState) { 97 | switch (hmsState) { 98 | case "changing": 99 | return Characteristic.SecuritySystemTargetState.DISARM; 100 | case "home": 101 | return Characteristic.SecuritySystemTargetState.STAY_ARM; 102 | case "away": 103 | return Characteristic.SecuritySystemTargetState.AWAY_ARM; 104 | case "disarm": 105 | return Characteristic.SecuritySystemTargetState.DISARM; 106 | } 107 | } 108 | convertHomeKitStateToHmsState(homeKitState) { 109 | switch (homeKitState) { 110 | case Characteristic.SecuritySystemTargetState.STAY_ARM: 111 | case Characteristic.SecuritySystemTargetState.NIGHT_ARM: 112 | return "home"; 113 | case Characteristic.SecuritySystemTargetState.AWAY_ARM: 114 | return "away"; 115 | case Characteristic.SecuritySystemTargetState.DISARM: 116 | return "off"; 117 | case Characteristic.SecuritySystemCurrentState.ALARM_TRIGGERED: 118 | return ""; 119 | } 120 | } 121 | 122 | async getHmsID() { 123 | if (this.hmsId == null || this.hmsId == "undefined") { 124 | const response = await this.plugin.client.getPlanBindingListByUser(); 125 | this.hmsId = response.data[0].deviceList[0].device_id; 126 | return this.hmsId; 127 | } else return this.hmsId; 128 | } 129 | }; 130 | -------------------------------------------------------------------------------- /src/accessories/WyzeContactSensor.js: -------------------------------------------------------------------------------- 1 | const { Service, Characteristic } = require("../types"); 2 | const WyzeAccessory = require("./WyzeAccessory"); 3 | 4 | const noResponse = new Error("No Response"); 5 | noResponse.toString = () => { 6 | return noResponse.message; 7 | }; 8 | 9 | module.exports = class WyzeContactSensor extends WyzeAccessory { 10 | constructor(plugin, homeKitAccessory) { 11 | super(plugin, homeKitAccessory); 12 | 13 | this.getOnCharacteristic(); 14 | this.getBatteryCharacteristic(); 15 | this.getIsBatteryLowCharacteristic(); 16 | } 17 | 18 | getSensorService() { 19 | if (this.plugin.config.pluginLoggingEnabled) 20 | this.plugin.log( 21 | `[ContactSensor] Retrieving previous service for "${this.display_name} (${this.mac})"` 22 | ); 23 | let service = this.homeKitAccessory.getService(Service.ContactSensor); 24 | 25 | if (!service) { 26 | if (this.plugin.config.pluginLoggingEnabled) 27 | this.plugin.log( 28 | `[ContactSensor] Adding service for "${this.display_name} (${this.mac})"` 29 | ); 30 | service = this.homeKitAccessory.addService(Service.ContactSensor); 31 | } 32 | 33 | return service; 34 | } 35 | 36 | getBatterySensorService() { 37 | if (this.plugin.config.pluginLoggingEnabled) 38 | this.plugin.log( 39 | `[ContactSensor] [Battery] Retrieving previous service for "${this.display_name} (${this.mac})"` 40 | ); 41 | let service = this.homeKitAccessory.getService(Service.Battery); 42 | if (!service) { 43 | if (this.plugin.config.pluginLoggingEnabled) 44 | this.plugin.log( 45 | `[ContactSensor] [Battery] Adding service for "${this.display_name} (${this.mac})"` 46 | ); 47 | service = this.homeKitAccessory.addService(Service.Battery); 48 | } 49 | 50 | return service; 51 | } 52 | 53 | getIsBatteryLowSensorService() { 54 | if (this.plugin.config.pluginLoggingEnabled) 55 | this.plugin.log( 56 | `[ContactSensor] [Low Battery] Retrieving previous service for "${this.display_name} (${this.mac})"` 57 | ); 58 | let service = this.homeKitAccessory.getService(Service.Battery); 59 | 60 | if (!service) { 61 | if (this.plugin.config.pluginLoggingEnabled) 62 | this.plugin.log( 63 | `[ContactSensor] [Low Battery] Adding service for "${this.display_name} (${this.mac})"` 64 | ); 65 | service = this.homeKitAccessory.addService(Service.Battery); 66 | } 67 | 68 | return service; 69 | } 70 | 71 | getOnCharacteristic() { 72 | if (this.plugin.config.pluginLoggingEnabled) 73 | this.plugin.log( 74 | `[ContactSensor] Fetching status of "${this.display_name} (${this.mac})"` 75 | ); 76 | return this.getSensorService().getCharacteristic( 77 | Characteristic.ContactSensorState 78 | ); 79 | } 80 | 81 | getBatteryCharacteristic() { 82 | if (this.plugin.config.pluginLoggingEnabled) 83 | this.plugin.log( 84 | `[ContactSensor] [Battery] Fetching status of "${this.display_name} (${this.mac})"` 85 | ); 86 | return this.getBatterySensorService().getCharacteristic( 87 | Characteristic.BatteryLevel 88 | ); 89 | } 90 | 91 | getIsBatteryLowCharacteristic() { 92 | if (this.plugin.config.pluginLoggingEnabled) 93 | this.plugin.log( 94 | `[ContactSensor] [Low Battery] Fetching status of "${this.display_name} (${this.mac})"` 95 | ); 96 | return this.getIsBatteryLowSensorService().getCharacteristic( 97 | Characteristic.StatusLowBattery 98 | ); 99 | } 100 | 101 | updateCharacteristics(device) { 102 | if (device.conn_state === 0) { 103 | if (this.plugin.config.pluginLoggingEnabled) 104 | this.plugin.log( 105 | `[ContactSensor] Updating status "${this.display_name} (${this.mac}) to noResponse"` 106 | ); 107 | this.getOnCharacteristic().updateValue(noResponse); 108 | } else { 109 | if (this.plugin.config.pluginLoggingEnabled) 110 | this.plugin.log( 111 | `[ContactSensor] Updating status of ${this.mac} (${this.display_name})` 112 | ); 113 | this.getOnCharacteristic().updateValue( 114 | device.device_params.open_close_state 115 | ); 116 | if (this.plugin.config.pluginLoggingEnabled) 117 | this.plugin.log( 118 | `[ContactSensor] [Battery] Updating status of ${this.mac} (${ 119 | this.display_name 120 | }) : ${this.plugin.client.checkBatteryVoltage( 121 | device.device_params.voltage 122 | )}` 123 | ); 124 | this.getBatteryCharacteristic().updateValue( 125 | this.plugin.client.checkBatteryVoltage(device.device_params.voltage) 126 | ); 127 | if (this.plugin.config.pluginLoggingEnabled) 128 | this.plugin.log( 129 | `[ContactSensor] [Low Battery] Updating status of ${this.mac} (${ 130 | this.display_name 131 | }) : ${this.plugin.client.checkLowBattery( 132 | device.device_params.voltage 133 | )}` 134 | ); 135 | this.getIsBatteryLowCharacteristic().updateValue( 136 | this.plugin.client.checkLowBattery(device.device_params.voltage) 137 | ); 138 | } 139 | } 140 | }; 141 | -------------------------------------------------------------------------------- /src/accessories/WyzeLight.js: -------------------------------------------------------------------------------- 1 | const { Service, Characteristic } = require("../types"); 2 | const WyzeAccessory = require("./WyzeAccessory"); 3 | 4 | const WYZE_API_BRIGHTNESS_PROPERTY = "P1501"; 5 | const WYZE_API_COLOR_TEMP_PROPERTY = "P1502"; 6 | const WYZE_COLOR_TEMP_MIN = 2700; 7 | const WYZE_COLOR_TEMP_MAX = 6500; 8 | const HOMEKIT_COLOR_TEMP_MIN = 500; 9 | const HOMEKIT_COLOR_TEMP_MAX = 140; 10 | 11 | const noResponse = new Error("No Response"); 12 | noResponse.toString = () => { 13 | return noResponse.message; 14 | }; 15 | 16 | module.exports = class WyzeLight extends WyzeAccessory { 17 | constructor(plugin, homeKitAccessory) { 18 | super(plugin, homeKitAccessory); 19 | 20 | this.getCharacteristic(Characteristic.On).on("set", this.setOn.bind(this)); 21 | this.getCharacteristic(Characteristic.Brightness).on( 22 | "set", 23 | this.setBrightness.bind(this) 24 | ); 25 | this.getCharacteristic(Characteristic.ColorTemperature).on( 26 | "set", 27 | this.setColorTemperature.bind(this) 28 | ); 29 | } 30 | 31 | async updateCharacteristics(device) { 32 | if (device.conn_state === 0) { 33 | if (this.plugin.config.pluginLoggingEnabled) 34 | this.plugin.log( 35 | `[Light] Updating status ${this.mac} (${this.display_name}) to noResponse` 36 | ); 37 | this.getCharacteristic(Characteristic.On).updateValue(noResponse); 38 | } else { 39 | if (this.plugin.config.pluginLoggingEnabled) { 40 | this.plugin.log( 41 | `[Light] Updating status of ${this.mac} (${this.display_name})` 42 | ); 43 | } 44 | this.getCharacteristic(Characteristic.On).updateValue( 45 | device.device_params.switch_state 46 | ); 47 | 48 | const propertyList = await this.plugin.client.getDevicePID( 49 | this.mac, 50 | this.product_model 51 | ); 52 | for (const property of propertyList.data.property_list) { 53 | switch (property.pid) { 54 | case WYZE_API_BRIGHTNESS_PROPERTY: 55 | this.updateBrightness(property.value); 56 | break; 57 | 58 | case WYZE_API_COLOR_TEMP_PROPERTY: 59 | this.updateColorTemp(property.value); 60 | break; 61 | } 62 | } 63 | } 64 | } 65 | 66 | updateBrightness(value) { 67 | if (this.plugin.config.pluginLoggingEnabled) { 68 | this.plugin.log( 69 | `[Light] Updating brightness of ${this.mac} (${this.display_name})` 70 | ); 71 | } 72 | this.getCharacteristic(Characteristic.Brightness).updateValue(value); 73 | } 74 | 75 | updateColorTemp(value) { 76 | if (this.plugin.config.pluginLoggingEnabled) 77 | this.plugin.log( 78 | `[Light] Setting color temperature for ${this.mac} (${ 79 | this.display_name 80 | }) to ${value} (${this.plugin.client.kelvinToMired(value)})` 81 | ); 82 | this.getCharacteristic(Characteristic.ColorTemperature).updateValue( 83 | this.plugin.client.kelvinToMired(value) 84 | ); 85 | } 86 | 87 | getService() { 88 | let service = this.homeKitAccessory.getService(Service.Lightbulb); 89 | 90 | if (!service) { 91 | service = this.homeKitAccessory.addService(Service.Lightbulb); 92 | } 93 | 94 | return service; 95 | } 96 | 97 | getCharacteristic(characteristic) { 98 | return this.getService().getCharacteristic(characteristic); 99 | } 100 | 101 | sleep(ms) { 102 | return new Promise((resolve) => setTimeout(resolve, ms)); 103 | } 104 | 105 | async setOn(value, callback) { 106 | if (this.plugin.config.pluginLoggingEnabled) 107 | this.plugin.log( 108 | `[Light] Setting power for ${this.mac} (${this.display_name}) to ${value}` 109 | ); 110 | 111 | try { 112 | await this.plugin.client.lightPower( 113 | this.mac, 114 | this.product_model, 115 | value ? "1" : "0" 116 | ); 117 | callback(); 118 | } catch (e) { 119 | callback(e); 120 | } 121 | } 122 | 123 | async setBrightness(value, callback) { 124 | await this.sleep(250); 125 | if (this.plugin.config.pluginLoggingEnabled) 126 | this.plugin.log( 127 | `[Light] Setting brightness for ${this.mac} (${this.display_name}) to ${value}` 128 | ); 129 | 130 | try { 131 | await this.plugin.client.setBrightness( 132 | this.mac, 133 | this.product_model, 134 | value 135 | ); 136 | callback(); 137 | } catch (e) { 138 | callback(e); 139 | } 140 | } 141 | 142 | // TODO: Issues when Color Temp higher then 143 | async setColorTemperature(value, callback) { 144 | await this.sleep(500); 145 | const floatValue = this.plugin.client.rangeToFloat( 146 | value, 147 | HOMEKIT_COLOR_TEMP_MIN, 148 | HOMEKIT_COLOR_TEMP_MAX 149 | ); 150 | const wyzeValue = this.plugin.client.floatToRange( 151 | floatValue, 152 | WYZE_COLOR_TEMP_MIN, 153 | WYZE_COLOR_TEMP_MAX 154 | ); 155 | if (this.plugin.config.pluginLoggingEnabled) 156 | this.plugin.log( 157 | `[Light] Setting color temperature for ${this.mac} (${this.display_name}) to ${value} (${wyzeValue})` 158 | ); 159 | 160 | try { 161 | await this.plugin.client.setColorTemperature( 162 | this.mac, 163 | this.product_model, 164 | wyzeValue 165 | ); 166 | callback(); 167 | } catch (e) { 168 | callback(e); 169 | } 170 | } 171 | }; 172 | -------------------------------------------------------------------------------- /src/accessories/WyzeTemperatureHumidity.js: -------------------------------------------------------------------------------- 1 | const { Service, Characteristic } = require("../types"); 2 | const WyzeAccessory = require("./WyzeAccessory"); 3 | 4 | const noResponse = new Error("No Response"); 5 | noResponse.toString = () => { 6 | return noResponse.message; 7 | }; 8 | 9 | module.exports = class WyzeTemperatureHumidity extends WyzeAccessory { 10 | constructor(plugin, homeKitAccessory) { 11 | super(plugin, homeKitAccessory); 12 | 13 | this.getTemperatureCharacteristic(); 14 | this.getHumidityCharacteristic(); 15 | this.getBatteryCharacteristic(); 16 | this.getIsBatteryLowCharacteristic(); 17 | } 18 | 19 | getHumiditySensorService() { 20 | if (this.plugin.config.pluginLoggingEnabled) 21 | this.plugin.log( 22 | `[Humidity] Retrieving previous service for "${this.display_name} (${this.mac})"` 23 | ); 24 | let service = this.homeKitAccessory.getService(Service.HumiditySensor); 25 | 26 | if (!service) { 27 | if (this.plugin.config.pluginLoggingEnabled) 28 | this.plugin.log( 29 | `[Humidity] Adding service for "${this.display_name} (${this.mac})"` 30 | ); 31 | service = this.homeKitAccessory.addService(Service.HumiditySensor); 32 | } 33 | 34 | return service; 35 | } 36 | 37 | getTemperatureSensorService() { 38 | if (this.plugin.config.pluginLoggingEnabled) 39 | this.plugin.log( 40 | `[Temperature] Retrieving previous service for "${this.display_name} (${this.mac})"` 41 | ); 42 | let service = this.homeKitAccessory.getService(Service.TemperatureSensor); 43 | 44 | if (!service) { 45 | if (this.plugin.config.pluginLoggingEnabled) 46 | this.plugin.log( 47 | `[Temperature] Adding service for "${this.display_name} (${this.mac})"` 48 | ); 49 | service = this.homeKitAccessory.addService(Service.TemperatureSensor); 50 | } 51 | 52 | return service; 53 | } 54 | 55 | getBatterySensorService() { 56 | if (this.plugin.config.pluginLoggingEnabled) 57 | this.plugin.log( 58 | `[Temperature Humidity] [Battery] Retrieving previous service for "${this.display_name} (${this.mac})"` 59 | ); 60 | let service = this.homeKitAccessory.getService(Service.Battery); 61 | 62 | if (!service) { 63 | if (this.plugin.config.pluginLoggingEnabled) 64 | this.plugin.log( 65 | `[Temperature Humidity] [Battery] Adding service for "${this.display_name} (${this.mac})"` 66 | ); 67 | service = this.homeKitAccessory.addService(Service.Battery); 68 | } 69 | 70 | return service; 71 | } 72 | 73 | getIsBatteryLowSensorService() { 74 | if (this.plugin.config.pluginLoggingEnabled) 75 | this.plugin.log( 76 | `[Temperature Humidity] [Low Battery] Retrieving previous service for "${this.display_name} (${this.mac})"` 77 | ); 78 | let service = this.homeKitAccessory.getService(Service.Battery); 79 | 80 | if (!service) { 81 | if (this.plugin.config.pluginLoggingEnabled) 82 | this.plugin.log( 83 | `[Temperature Humidity] [Low Battery] Adding service for "${this.display_name} (${this.mac})"` 84 | ); 85 | service = this.homeKitAccessory.addService(Service.Battery); 86 | } 87 | 88 | return service; 89 | } 90 | 91 | getHumidityCharacteristic() { 92 | if (this.plugin.config.pluginLoggingEnabled) 93 | this.plugin.log( 94 | `[Temperature Humidity] Fetching status of "${this.display_name} (${this.mac})"` 95 | ); 96 | return this.getHumiditySensorService().getCharacteristic( 97 | Characteristic.CurrentRelativeHumidity 98 | ); 99 | } 100 | 101 | getTemperatureCharacteristic() { 102 | if (this.plugin.config.pluginLoggingEnabled) 103 | this.plugin.log( 104 | `[Temperature Humidity] Fetching status of "${this.display_name} (${this.mac})"` 105 | ); 106 | return this.getTemperatureSensorService().getCharacteristic( 107 | Characteristic.CurrentTemperature 108 | ); 109 | } 110 | 111 | getBatteryCharacteristic() { 112 | if (this.plugin.config.pluginLoggingEnabled) 113 | this.plugin.log( 114 | `[Temperature Humidity] [Battery] Fetching status of "${this.display_name} (${this.mac})"` 115 | ); 116 | return this.getBatterySensorService().getCharacteristic( 117 | Characteristic.BatteryLevel 118 | ); 119 | } 120 | 121 | getIsBatteryLowCharacteristic() { 122 | if (this.plugin.config.pluginLoggingEnabled) 123 | this.plugin.log( 124 | `[Temperature Humidity] [Low Battery] Fetching status of "${this.display_name} (${this.mac})"` 125 | ); 126 | return this.getIsBatteryLowSensorService().getCharacteristic( 127 | Characteristic.StatusLowBattery 128 | ); 129 | } 130 | 131 | updateCharacteristics(device) { 132 | if (this.plugin.config.pluginLoggingEnabled) 133 | this.plugin.log( 134 | `[Temperature Humidity] Updating status of "${this.display_name} (${this.mac})"` 135 | ); 136 | if (device.conn_state === 0) { 137 | this.getHumidityCharacteristic().updateValue(noResponse); 138 | } else { 139 | this.getHumidityCharacteristic().updateValue( 140 | device.device_params.th_sensor_humidity 141 | ); 142 | this.getTemperatureCharacteristic().updateValue( 143 | (device.device_params.th_sensor_temperature - 32.0) / 1.8 144 | ); 145 | this.getBatteryCharacteristic().updateValue( 146 | this.plugin.client.checkBatteryVoltage(device.device_params.voltage) 147 | ); 148 | this.getIsBatteryLowCharacteristic().updateValue( 149 | this.plugin.client.checkLowBattery(device.device_params.voltage) 150 | ); 151 | } 152 | } 153 | }; 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This plugin adds support for Wyze Smart Home devices to [Homebridge](https://github.com/homebridge/homebridge). 2 | [![verified-by-homebridge](https://badgen.net/badge/homebridge/verified/purple)](https://github.com/homebridge/homebridge/wiki/Verified-Plugins) 3 | [![certified-hoobs-plugin](https://badgen.net/badge/HOOBS/certified/yellow)](https://plugins.hoobs.org/plugin/homebridge-wyze-smart-home) 4 | [![npm](https://img.shields.io/npm/dt/homebridge-wyze-smart-home)](https://www.npmjs.com/package/homebridge-wyze-smart-home) 5 | [![npm](https://img.shields.io/npm/v/homebridge-wyze-smart-home.svg?style=flat-square)](https://www.npmjs.com/package/homebridge-wyze-smart-home) 6 | [![GitHub last commit](https://img.shields.io/github/last-commit/jfarmer08/homebridge-wyze-smart-home)](https://github.com/jfarmer08/homebridge-wyze-smart-home) 7 | [![Chat](https://img.shields.io/discord/1134601590762913863)](https://discord.gg/Mjkpq2x9) 8 | 9 | [![homebridge-wyze-smart-home: Wyze Connected Home plugin for Homebridge](https://github.com/jfarmer08/homebridge-wyze-smart-home/blob/main/logo.png?raw=true)](https://github.com/jfarmer08/homebridge-wyze-smart-home) 10 | 11 | [Major Feature Backlog/Status](https://github.com/jfarmer08/homebridge-wyze-smart-home/discussions/224) 12 | 13 | 14 | # Funding [![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square&maxAge=2592000)](https://www.paypal.com/paypalme/AllenFarmer) [![Donate](https://img.shields.io/badge/Donate-Venmo-blue.svg?style=flat-square&maxAge=2592000)](https://venmo.com/u/Allen-Farmer) [![Donate](https://img.shields.io/badge/Donate-Cash_App-blue.svg?style=flat-square&maxAge=2592000)](https://cash.app/$Jfamer08) 15 | If you like what I have done here and want to help I would recommend that you firstly look into supporting Homebridge. None of this could happen without them. 16 | 17 | After you have done that if you feel like my work has been valuable to you I welcome your support through Paypal, Venmo or Cash App. 18 | 19 | ## Supported Devices 20 | - Light Bulb 21 | - Light Strips 22 | - Color Bulb (Mesh Light) 23 | - Plug 24 | - Outdoor Plug 25 | - V1 & V2 Contact Sensor (Status / Battery) 26 | - V1 & V2 Motion Sensor (Status / Battery) 27 | - Tempeature Sensor (Status / Battery) 28 | - Leak Sensor (Status / Battery) 29 | - Lock (Battery / Door Status / Control) 30 | - Camera v2, v3, Outdoor Cam, PamCam (on/off, Siren, Floodlight, Garage Door) 31 | - Wall Switch 32 | - HMS 33 | - Thermostat 34 | 35 | For more information about our version updates, please check our [change log](CHANGELOG.md). 36 | 37 | ## Configuration 38 | 39 | Use the settings UI in Homebridge Config UI X to configure your Wyze account, or manually add the following to the platforms section of your config file: 40 | 41 | ```js 42 | { 43 | "platforms": [ 44 | { 45 | "platform": "WyzeSmartHome", 46 | "name": "Wyze", 47 | "username": "YOUR_EMAIL", 48 | "password": "YOUR_PASSWORD", 49 | "keyId": "", 50 | "apiKey": "", 51 | "lowBatteryPercentage": 30, 52 | "filterDeviceTypeList": ["OutdoorPlug","Plug"], 53 | "filterByMacAddressList": ["MAC_ADDRESS_1","MAC_ADDRESS_2"], 54 | "garageDoorAccessory": ["MAC_ADDRESS_1","MAC_ADDRESS_2"], 55 | "spotLightAccessory": ["MAC_ADDRESS_1","MAC_ADDRESS_2"], 56 | "alarmAccessory": ["MAC_ADDRESS_1","MAC_ADDRESS_2"], 57 | "notificationAccessory": ["MAC_ADDRESS_1","MAC_ADDRESS_2"]} 58 | ] 59 | } 60 | ``` 61 | 62 | Supported devices will be discovered and added to Homebridge automatically. 63 | 64 | ### Required Fields 65 | 66 | * **`username`** – App email address 67 | * **`password`** – App password 68 | * **`apiKey`** – Navigate to [this portal](https://developer-api-console.wyze.com/) 69 | * **`keyId`** – Navigate to [this portal](https://developer-api-console.wyze.com/), and click Login to sign in to your Wyze account. 70 | Note: Ensure that the login info you are using matches the info you use when logLevel into the Wyze app. 71 | Once you’ve signed in, you’ll be automatically redirected back to the developer page. 72 | Click Create an API key for your API key to be created. 73 | Once created, you can click view to see the entire key. 74 | You should receive an email that a new API key has been generated. 75 | Once you have the API key, you can use it in your script to get the access token and refresh token. 76 | 77 | ### Optional Fields 78 | 79 | * **`refreshInterval`** – Defines how often the status of the devices will be polled in milliseconds (e.g., `"refreshInterval": 60000` will check the status of your devices' status every 60 seconds). Defaults to 60 seconds. 80 | * **`phoneId`** – The phone id used by the Wyze App. This value is just found by intercepting your phone's traffic. If no `phoneId` is specified, a default value will be used. 81 | * **`logLevel`** – If no `logLevel` is specified, a default value will be used. 82 | * **`apiLogEnabled`** – If no `apiLogEnabled` is specified, a default value will be used. 83 | * **`authApiKey`** – If no `authApiKey` is specified, a default value will be used. 84 | * **`appName`** – If no `appName` is specified, a default value will be used. 85 | * **`appVer`** – If no `appVer` is specified, a default value will be used. 86 | * **`appVersion`** – If no `appVersion` is specified, a default value will be used. 87 | * **`userAgent`** – If no `userAgent` is specified, a default value will be used. 88 | * **`sc`** – If no `sc` is specified, a default value will be used. 89 | * **`sv`** – If no `sv` is specified, a default value will be used. 90 | * **`persistPath`** – If no `persistPath` is specified, a default value will be used. 91 | * **`refreshTokenTimerEnabled`** – If no `refreshTokenTimerEnabled` is specified, a default value will be used. 92 | * **`lowBatteryPercentage`** – Defines when to show devices with low battery (e.g., `"lowBatteryPercentage": 30`). Defaults to 30%. 93 | 94 | ## Other Info 95 | 96 | Special thanks to the following projects for reference and inspiration: 97 | 98 | - [ha-wyzeapi](https://github.com/JoshuaMulliken/ha-wyzeapi), a Wyze integration for Home Assistant. 99 | - [wyze-node](https://github.com/noelportugal/wyze-node), a Node library for the Wyze API. 100 | 101 | Thanks to [misenhower](https://github.com/misenhower/homebridge-wyze-connected-home) for the original Wyze Homebridge plugin, and thanks to [contributors](https://github.com/misenhower/homebridge-wyze-connected-home/graphs/contributors) and [other developers who were not merged](https://github.com/misenhower/homebridge-wyze-connected-home/pulls) for volunteering their time to help fix bugs and add support for more devices and features. 102 | 103 | This plugin is an actively maintained fork of misenhower's original [Wyze Homebridge Plugin](https://github.com/misenhower/homebridge-wyze-connected-home) project. 104 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # homebridge-wyze-smart-home 2 | 3 | # Funding [![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square&maxAge=2592000)](https://www.paypal.com/paypalme/AllenFarmer) [![Donate](https://img.shields.io/badge/Donate-Venmo-blue.svg?style=flat-square&maxAge=2592000)](https://venmo.com/u/Allen-Farmer) [![Donate](https://img.shields.io/badge/Donate-Cash_App-blue.svg?style=flat-square&maxAge=2592000)](https://cash.app/$Jfamer08) 4 | 5 | If you like what I have done here and want to help I would recommend that you firstly look into supporting Homebridge. None of this could happen without them. 6 | 7 | After you have done that if you feel like my work has been valuable to you I welcome your support through Paypal or other means. 8 | 9 | ## Releases 10 | 11 | ### v0.5.46 12 | - Update thermostat structure to new format 13 | - Update thermostat to have min/max thresholds closer to Wyze Thermostat thresholds - https://github.com/jfarmer08/homebridge-wyze-smart-home/issues/203 14 | - Update thermostat to avoid constant reboot - https://github.com/jfarmer08/homebridge-wyze-smart-home/issues/228 15 | 16 | ### v0.5.45 17 | - Increase Wyze-api Verison 1.0.7 18 | - Update Logging 19 | 20 | ### v0.5.44 21 | - Increase Wyze-api Version 1.0.5 22 | 23 | ### v0.5.43 24 | - Increase Wyze-api Version 1.0.6 25 | 26 | ### v0.5.42 27 | ### v0.5.41 28 | - Increase version of wyze-api 1.0.3 29 | 30 | ### v0.5.40 31 | - Correct Outdoor Cam 2 model number 32 | 33 | ### v0.5.39 34 | - Support for WyzeCamOutdoor2 35 | 36 | ### v0.5.38 37 | - Resolve issues with API changes from Wyze (2024-02-01) by @hgoscenski in #3 38 | - Format Code 39 | 40 | ### v0.5.37-alpha.7 41 | - Remove Delay from MeshLight 42 | - Format Code 43 | 44 | ### v0.5.37-alpha.6 45 | - Correct Mesh Brightness 46 | - Correct camera offline 47 | 48 | ### v0.5.37-alpha.5 49 | - Code Clean up 50 | - Add Switch to turn on/off Notifications 51 | - Correct Wall Switch Status 52 | - Adjust Logging 53 | 54 | ### v0.5.37-alpha.4 55 | - Add HL_A19C2 56 | - Code clean up 57 | 58 | ### v0.5.37-alpha.3 59 | - Fix issue with Light 60 | 61 | ### v0.5.37-alpha.2 62 | - Add check box for HMS Subscription 63 | - Removed MFA Support 64 | - Correct Door State for Garage Door 65 | - Code clean up 66 | 67 | ### v0.5.37-alpha.1 68 | - Support for Siren 69 | - Support for Garage Door 70 | - Support for Spotlight 71 | - Support for Floodlight 72 | 73 | ### v0.5.36 74 | - Add HL_Cam3p to Approved List 75 | - Require API Key and KeyID 76 | - Add Info Logging 77 | 78 | ### v0.5.35 79 | - Add loging to sub models 80 | 81 | ### v0.5.34 82 | - Allow all sub models. 83 | 84 | ### v0.5.33 85 | - Updated Thermostat behavior for single mode usage (heat/cool) (Thanks https://github.com/carTloyal123) - https://github.com/jfarmer08/homebridge-wyze-smart-home/issues/111 86 | 87 | ### v0.5.32 88 | - Improve logging - https://github.com/jfarmer08/homebridge-wyze-smart-home/issues/117 89 | 90 | ### v0.5.31 91 | - Improve Camera support - https://github.com/jfarmer08/homebridge-wyze-smart-home/issues/92 92 | 93 | ### v0.5.30 94 | - HMS Code Clean up 95 | - Update ReadME 96 | - Update WyzeLeakSensor 97 | 98 | ### v0.5.29 99 | - Support for API Key and Key ID 100 | - Support for WYZECP1_JEF 101 | 102 | ### v0.5.28 103 | - Correction for No-Response 104 | 105 | ### v0.5.26 106 | - Release of Beta 107 | 108 | ### v0.5.25-beta.5 109 | - Support for Thermostat 110 | - Support for Wall Switch 111 | - Support for HMS 112 | 113 | ### v0.5.25-dev.0 114 | - Support for Thermostat 115 | - Support for Wall Switch 116 | - Support for HMS 117 | ### v0.5.25-beta.3 118 | - Wall Switch Status update 119 | - HMS 120 | - Lock support is broken 121 | ### v0.5.25-beta.2 122 | - Wall Switch was not status being followed 123 | - Unable to turn Wall Switch On or Off. 124 | - LOCK support is broken for this release 125 | ### v0.5.25-beta.1 126 | - Wall Switch Support 127 | - Lock changes - Reduce calls to wyze platform 128 | - Major Changes to SDK 129 | - Initial support for Thermostat in SDK 130 | - Initial support for HMS in SDK. 131 | 132 | ### v0.5.24 133 | - Release 134 | 135 | ### v0.5.24-beta.1 136 | - Filter Devices by Mac Address (Thanks https://github.com/kliu99) 137 | - Filter Devices by Device Type 138 | - Refresh refreshToken every 48 Hours 139 | - Add Logging 140 | 141 | ### v0.5.24-beta.0 142 | - Feature Support for ignoring devices 143 | - Upate default refresh interval to 30 secounds 144 | - Update grammer error 145 | 146 | ### v0.5.23 147 | - Bug OutDoor Camera was not working with on/off 148 | - Bug Wyze Doorbell does not support on/off 149 | 150 | ### v0.5.22 151 | - Update NPM Version 152 | 153 | ### v0.5.21 154 | - Battery Support for Locks 155 | - Door Sensor from lock now being reported 156 | - Update NPM Version 157 | - Change Log Update 158 | 159 | ### v0.5.20 160 | - Broke Offline Support 161 | 162 | ### v0.5.19 163 | - Issue with Locks after adding Camera Support 164 | 165 | ### v0.5.18 166 | - Initial Support for Camera on/off switch 167 | - Code Clean up 168 | 169 | ### v0.5.17 170 | - Initial Support for noResponse when device is offline. 171 | ContactSensor v2 172 | LeakSensor v2 173 | Light Bulb 174 | Mesh Light Bulb 175 | Motion Sensor 176 | Plug 177 | - Initial Support for Battery Level on Leak Sensor 178 | 179 | ### v0.5.15 180 | - Bug Sensor can send a value greater then 100 for Battery Level 181 | - v0.5.14 Initial Support for Battery level on Temperature Sensor 182 | - v0.5.14 Initial Support for Battery level on v2 Contact Sensor 183 | - v0.5.14 Initial Support for Battery level on v2 Motion Sensor 184 | - v0.5.13 Fix issue with Temperature Sensor 185 | - v0.5.12 Fix issue with Leak Sensor 186 | - v0.5.11 Fix issue with Motion Sensor 187 | - v0.5.10 Initial support for Wyze Temperature Sensor 188 | - v0.5.10 Initial support for Wyze Leak Sensor 189 | - v0.5.9 Initial support for Wyze Light Strips 190 | - v0.5.9 Initial support for Wyze V2 Contact & Motion sensors 191 | - v0.5.8 Fixed Bulbs not properly changing values when in a Scene with other Bulbs 192 | - v0.5.8 Improved & streamlined logging (moved all status changes to Debug logs) 193 | - v0.5.7 Initial support for the Wyze Lock 194 | - v0.5.6 Initial support for new Wyze Color Bulbs 195 | - v0.5.3 Improve logfile output for Bulb and Outdoor Plug 196 | - v0.5.2 Added support for Wyze Outdoor Plug 197 | - v0.5.1 Improve debug logging for Contact and Motion sensors. 198 | - v0.5.0 Added support to Contact and Motion sensors 199 | - v0.5.0 Added support to two factor authentication (2FA) via Authenticator app 200 | - v0.4.1 Fix an issue that prevented the auto re-login from working 201 | - v0.4.0 Add experimental support for the Wyze Bulb accessory 202 | - v0.4.0 Set the homepage property 203 | - v0.4.0 Improve logging to help diagnose occasional login issues 204 | - v0.3.0 Add config schema for Homebridge Config UI X 205 | - v0.2.0 Fix an issue caused by the Wyze API lagging behind updates 206 | - v0.2.0 Fix description 207 | - v0.2.0 Fix project link 208 | - v0.1.0 Initial commit 209 | -------------------------------------------------------------------------------- /config.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginAlias": "WyzeSmartHome", 3 | "pluginType": "platform", 4 | "singular": true, 5 | "headerDisplay": "Wyze plug-in for Homebridge. This is a custom component to allow control of various Wyze devices using the unofficial API. Please note this mimics the Wyze app and therefore access may be cut off at anytime. If you feel like my work has been valuable to you I welcome your support through Paypal. All funding goes back into product purchases. [![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg?style=flat-square&maxAge=2592000)](https://www.paypal.com/paypalme/AllenFarmer)", 6 | "footerDisplay": "For documentation please see GitHub repository", 7 | "schema": { 8 | "type": "object", 9 | "properties": { 10 | "name": { 11 | "title": "Plugin Name", 12 | "description": "This name will appear in your Homebridge log", 13 | "type": "string", 14 | "default": "Wyze", 15 | "required": true 16 | }, 17 | "username": { 18 | "title": "Username (E-Mail Address)", 19 | "description": "The e-mail address used for your Wyze account", 20 | "type": "string", 21 | "default": "", 22 | "required": true 23 | }, 24 | "password": { 25 | "title": "Password", 26 | "description": "The password used for your Wyze account", 27 | "type": "string", 28 | "default": "", 29 | "required": true 30 | }, 31 | "keyId": { 32 | "title": "Key ID", 33 | "description": "API Key/ID available from the official Wyze Portal https://developer-api-console.wyze.com", 34 | "type": "string", 35 | "required": true 36 | }, 37 | "apiKey": { 38 | "title": "API Key", 39 | "description": "API Key/ID available from the official Wyze Portal https://developer-api-console.wyze.com/)", 40 | "type": "string", 41 | "required": true 42 | }, 43 | "refreshInterval": { 44 | "title": "Refresh Interval", 45 | "description": "Specify the number of milliseconds to wait between updates, default is 60000 ms (60 seconds)", 46 | "type": "integer", 47 | "default": 60000, 48 | "minimum": 60000, 49 | "maximum": 180000, 50 | "multipleOf": 10000, 51 | "required": false 52 | }, 53 | "hms": { 54 | "title": "HMS Subscription", 55 | "description": "Enable if HMS Subscription is current", 56 | "type": "boolean", 57 | "default": false 58 | }, 59 | "showAdvancedOptions": { 60 | "title": "Advance Options", 61 | "default": false, 62 | "type": "boolean" 63 | }, 64 | "apiLogEnabled": { 65 | "title": "Enable API logging", 66 | "description": "", 67 | "type": "boolean", 68 | "default": false, 69 | "condition": { 70 | "functionBody": "return model.showAdvancedOptions === true;" 71 | } 72 | }, 73 | "pluginLoggingEnabled": { 74 | "title": "Enable Plugin logging", 75 | "description": "", 76 | "type": "boolean", 77 | "default": false, 78 | "condition": { 79 | "functionBody": "return model.showAdvancedOptions === true;" 80 | } 81 | }, 82 | "lowBatteryPercentage": { 83 | "title": "Low Battery Percentage", 84 | "description": "Specify the Percentage of battery when consider low, default is 30%", 85 | "type": "integer", 86 | "default": 30, 87 | "minimum": 10, 88 | "maximum": 100, 89 | "multipleOf": 1, 90 | "required": false, 91 | "condition": { 92 | "functionBody": "return model.showAdvancedOptions === true;" 93 | } 94 | }, 95 | "garageDoorAccessory": { 96 | "type": "array", 97 | "title": "Wyze Garage Door Controller", 98 | "items": { 99 | "title": "Device MAC Address with accessory attached", 100 | "type": "string" 101 | }, 102 | "condition": { 103 | "functionBody": "return model.showAdvancedOptions === true;" 104 | } 105 | }, 106 | "spotLightAccessory": { 107 | "type": "array", 108 | "title": "Wyze Camera Spotlight", 109 | "items": { 110 | "title": "Device MAC Address with accessory attached", 111 | "type": "string" 112 | }, 113 | "condition": { 114 | "functionBody": "return model.showAdvancedOptions === true;" 115 | } 116 | }, 117 | "floodLightAccessory": { 118 | "type": "array", 119 | "title": "Wyze Camera Floodlight", 120 | "items": { 121 | "title": "Device MAC Address with accessory attached", 122 | "type": "string" 123 | }, 124 | "condition": { 125 | "functionBody": "return model.showAdvancedOptions === true;" 126 | } 127 | }, 128 | "sirenAccessory": { 129 | "type": "array", 130 | "title": "Wyze Camera Siren", 131 | "items": { 132 | "title": "Device MAC Address with accessory attached", 133 | "type": "string" 134 | }, 135 | "condition": { 136 | "functionBody": "return model.showAdvancedOptions === true;" 137 | } 138 | }, 139 | "notificationAccessory": { 140 | "type": "array", 141 | "title": "Wyze Notification Switch", 142 | "items": { 143 | "title": "Device MAC Address of camera you want to control the Notification Switch", 144 | "type": "string" 145 | }, 146 | "condition": { 147 | "functionBody": "return model.showAdvancedOptions === true;" 148 | } 149 | }, 150 | "excludeMacAddress": { 151 | "title": "Exclude by MAC Address", 152 | "default": false, 153 | "type": "boolean", 154 | "condition": { 155 | "functionBody": "return model.showAdvancedOptions === true;" 156 | } 157 | }, 158 | "filterByMacAddressList": { 159 | "type": "array", 160 | "title": "", 161 | "items": { 162 | "title": "Device MAC Address", 163 | "type": "string" 164 | }, 165 | "condition": { 166 | "functionBody": "return model.excludeMacAddress === true;" 167 | } 168 | }, 169 | "excludedeviceType": { 170 | "title": "Exclude Device by Type", 171 | "default": false, 172 | "type": "boolean", 173 | "condition": { 174 | "functionBody": "return model.showAdvancedOptions === true;" 175 | } 176 | }, 177 | "filterDeviceTypeList": { 178 | "title": "Exclude Device by Type", 179 | "type": "array", 180 | "uniqueItems": true, 181 | "items": { 182 | "title": "Device Type", 183 | "type": "string", 184 | "enum": [ 185 | "OutdoorPlug", 186 | "Plug", 187 | "Light", 188 | "MeshLight", 189 | "LightStrip", 190 | "ContactSensor", 191 | "MotionSensor", 192 | "Lock", 193 | "TemperatureHumidity", 194 | "LeakSensor", 195 | "Camera", 196 | "Common" 197 | ] 198 | }, 199 | "condition": { 200 | "functionBody": "return model.excludedeviceType === true;" 201 | } 202 | } 203 | } 204 | }, 205 | "form": null, 206 | "display": null 207 | } 208 | -------------------------------------------------------------------------------- /src/accessories/WyzeLock.js: -------------------------------------------------------------------------------- 1 | const { Service, Characteristic } = require("../types"); 2 | const WyzeAccessory = require("./WyzeAccessory"); 3 | 4 | const noResponse = new Error("No Response"); 5 | noResponse.toString = () => { 6 | return noResponse.message; 7 | }; 8 | 9 | module.exports = class WyzeLock extends WyzeAccessory { 10 | constructor(plugin, homeKitAccessory) { 11 | super(plugin, homeKitAccessory); 12 | 13 | if (this.plugin.config.pluginLoggingEnabled) 14 | this.plugin.log( 15 | `[Lock] Retrieving previous service for "${this.display_name} (${this.mac})"` 16 | ); 17 | this.lockService = this.homeKitAccessory.getService(Service.LockMechanism); 18 | 19 | if (this.plugin.config.pluginLoggingEnabled) 20 | this.plugin.log( 21 | `[Lock] [Door Contact] Retrieving previous service for "${this.display_name} (${this.mac})"` 22 | ); 23 | this.contactService = this.homeKitAccessory.getService( 24 | Service.ContactSensor 25 | ); 26 | 27 | if (this.plugin.config.pluginLoggingEnabled) 28 | this.plugin.log( 29 | `[Lock] [Battery] Retrieving previous service for "${this.display_name} (${this.mac})"` 30 | ); 31 | this.batteryService = this.homeKitAccessory.getService(Service.Battery); 32 | 33 | if (!this.lockService) { 34 | if (this.plugin.config.pluginLoggingEnabled) 35 | this.plugin.log( 36 | `[Lock] Adding service for "${this.display_name} (${this.mac})"` 37 | ); 38 | this.lockService = this.homeKitAccessory.addService( 39 | Service.LockMechanism 40 | ); 41 | } 42 | 43 | if (!this.contactService) { 44 | if (this.plugin.config.pluginLoggingEnabled) 45 | this.plugin.log( 46 | `[Lock] [Door Contact] Adding service for "${this.display_name} (${this.mac})"` 47 | ); 48 | this.contactService = this.homeKitAccessory.addService( 49 | Service.ContactSensor 50 | ); 51 | } 52 | 53 | if (!this.batteryService) { 54 | if (this.plugin.config.pluginLoggingEnabled) 55 | this.plugin.log( 56 | `[Lock] [Battery] Adding service for "${this.display_name} (${this.mac})"` 57 | ); 58 | this.batteryService = this.homeKitAccessory.addService(Service.Battery); 59 | } 60 | 61 | this.batteryService 62 | .getCharacteristic(Characteristic.BatteryLevel) 63 | .onGet(this.getBatteryStatus.bind(this)); 64 | 65 | this.batteryService 66 | .getCharacteristic(Characteristic.StatusLowBattery) 67 | .onGet(this.getLowBatteryStatus.bind(this)); 68 | 69 | this.contactService 70 | .getCharacteristic(Characteristic.ContactSensorState) 71 | .onGet(this.getDoorStatus.bind(this)); 72 | 73 | this.lockService 74 | .getCharacteristic(Characteristic.LockCurrentState) 75 | .onGet(this.getLockCurrentState.bind(this)); 76 | 77 | this.lockService 78 | .getCharacteristic(Characteristic.LockTargetState) 79 | .onGet(this.getLockTargetState.bind(this)) 80 | .onSet(this.setLockTargetState.bind(this)); 81 | } 82 | 83 | async updateCharacteristics(device) { 84 | if (this.plugin.config.pluginLoggingEnabled) 85 | this.plugin.log( 86 | `[Lock] Updating status "${this.display_name} (${this.mac}) to noResponse"` 87 | ); 88 | if (device.conn_state === 0) { 89 | //this.getLockCurrentState().updateValue(noResponse) 90 | this.lockService 91 | .getCharacteristic(Characteristic.LockCurrentState) 92 | .updateValue(noResponse); 93 | } else { 94 | if (this.plugin.config.pluginLoggingEnabled) 95 | this.plugin.log( 96 | `[Lock] Updating status of "${this.display_name} (${this.mac})"` 97 | ); 98 | const propertyList = await this.plugin.client.getLockInfo( 99 | this.mac, 100 | this.product_model 101 | ); 102 | let lockProperties = propertyList.device; 103 | const prop_key = Object.keys(lockProperties); 104 | for (const element of prop_key) { 105 | const prop = element; 106 | switch (prop) { 107 | case "onoff_line": 108 | this.lockOnOffline = lockProperties[prop]; 109 | break; 110 | case "power": 111 | // Lock Battery 112 | this.batteryService 113 | .getCharacteristic(Characteristic.BatteryLevel) 114 | .updateValue( 115 | this.plugin.client.checkBatteryVoltage(lockProperties[prop]) 116 | ); 117 | this.lockPower = lockProperties[prop]; 118 | break; 119 | case "door_open_status": 120 | // Door Status 121 | this.lockService 122 | .getCharacteristic(Characteristic.ContactSensorState) 123 | .updateValue( 124 | this.plugin.client.getLockDoorState(lockProperties[prop]) 125 | ); 126 | this.door_open_status = lockProperties[prop]; 127 | break; 128 | case "trash_mode": 129 | this.trash_mode = lockProperties[prop]; 130 | break; 131 | } 132 | } 133 | let lockerStatusProperties = propertyList.device.locker_status; 134 | const prop_keyLock = Object.keys(lockerStatusProperties); 135 | for (const element of prop_keyLock) { 136 | const prop = element; 137 | switch (prop) { 138 | case "hardlock": 139 | // Door Locked Status 140 | this.lockService 141 | .getCharacteristic(Characteristic.LockCurrentState) 142 | .updateValue( 143 | this.plugin.client.getLockState(lockerStatusProperties[prop]) 144 | ); 145 | this.hardlock = lockerStatusProperties[prop]; 146 | break; 147 | } 148 | } 149 | } 150 | } 151 | 152 | async getLockCurrentState() { 153 | if (this.plugin.config.pluginLoggingEnabled) 154 | this.plugin.log( 155 | `[Lock] Getting Current State "${this.display_name} (${this.mac}) to ${this.hardlock}"` 156 | ); 157 | if (this.hardlock == 2) { 158 | return Characteristic.LockTargetState.UNSECURED; 159 | } else { 160 | return Characteristic.LockTargetState.SECURED; 161 | } 162 | } 163 | 164 | async getLockTargetState() { 165 | if (this.plugin.config.pluginLoggingEnabled) 166 | this.plugin.log( 167 | `[Lock] Getting Target State "${this.display_name} (${this.mac}) to ${this.hardlock}"` 168 | ); 169 | 170 | if (this.hardlock === 2) { 171 | return Characteristic.LockTargetState.UNSECURED; 172 | } else { 173 | return Characteristic.LockTargetState.SECURED; 174 | } 175 | } 176 | 177 | async getDoorStatus() { 178 | if (this.plugin.config.pluginLoggingEnabled) 179 | this.plugin.log( 180 | `[Lock] Getting Door Status "${this.display_name} (${this.mac}) to ${this.door_open_status}"` 181 | ); 182 | if (this.door_open_status == 1) { 183 | return Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; // 1 184 | } else { 185 | return Characteristic.ContactSensorState.CONTACT_DETECTED; // 0 186 | } 187 | } 188 | 189 | async getBatteryStatus() { 190 | if (this.plugin.config.pluginLoggingEnabled) 191 | this.plugin.log( 192 | `[Lock] Getting Battery Status "${this.display_name} (${this.mac}) to ${this.lockPower}"` 193 | ); 194 | return this.plugin.client.checkBatteryVoltage(this.lockPower); 195 | } 196 | 197 | async getLowBatteryStatus() { 198 | if (this.plugin.config.pluginLoggingEnabled) 199 | this.plugin.log( 200 | `[Lock] Getting Low Battery Status "${this.display_name} (${ 201 | this.mac 202 | }) to ${this.plugin.client.checkLowBattery(this.lockPower)}"` 203 | ); 204 | return this.plugin.client.checkLowBattery(this.lockPower); 205 | } 206 | 207 | async setLockTargetState(targetState) { 208 | if (this.plugin.config.pluginLoggingEnabled) 209 | this.plugin.log(`[Lock] Setting Target State "${targetState}"`); // this is zero or 1 210 | await this.plugin.client.controlLock( 211 | this.mac, 212 | this.product_model, 213 | targetState === Characteristic.LockCurrentState.SECURED 214 | ? "remoteLock" 215 | : "remoteUnlock" 216 | ); 217 | 218 | // Takes a few seconds for the lock command to actually update lock state property 219 | // Poll every second to see if the lock state has changed to what we expect, or time out after 30 attempts 220 | //await this.poll(async () => await this.getLockCurrentState(), currentState => currentState === targetState, 1000, 10) 221 | this.lockService.setCharacteristic( 222 | Characteristic.LockCurrentState, 223 | targetState === Characteristic.LockTargetState.SECURED 224 | ? Characteristic.LockCurrentState.SECURED 225 | : Characteristic.LockCurrentState.UNSECURED 226 | ); 227 | } 228 | 229 | async poll(fn, validate, interval, maxAttempts) { 230 | let attempts = 0; 231 | 232 | const executePoll = async (resolve, reject) => { 233 | const result = await fn(); 234 | attempts++; 235 | 236 | if (validate(result)) { 237 | return resolve(result); 238 | } else if (maxAttempts && maxAttempts === attempts) { 239 | return reject(new Error("Exceeded maximum attempts")); 240 | } else { 241 | setTimeout(executePoll, interval, resolve, reject); 242 | } 243 | }; 244 | 245 | return new Promise(executePoll); 246 | } 247 | }; 248 | -------------------------------------------------------------------------------- /src/accessories/WyzeMeshLight.js: -------------------------------------------------------------------------------- 1 | const colorsys = require("colorsys"); 2 | const { Service, Characteristic } = require("../types"); 3 | const WyzeAccessory = require("./WyzeAccessory"); 4 | 5 | const WYZE_API_BRIGHTNESS_PROPERTY = "P1501"; 6 | const WYZE_API_COLOR_TEMP_PROPERTY = "P1502"; 7 | const WYZE_API_COLOR_PROPERTY = "P1507"; 8 | 9 | const WYZE_COLOR_TEMP_MIN = 2700; 10 | const WYZE_COLOR_TEMP_MAX = 6500; 11 | const HOMEKIT_COLOR_TEMP_MIN = 500; 12 | const HOMEKIT_COLOR_TEMP_MAX = 140; 13 | 14 | const noResponse = new Error("No Response"); 15 | noResponse.toString = () => { 16 | return noResponse.message; 17 | }; 18 | 19 | module.exports = class WyzeMeshLight extends WyzeAccessory { 20 | constructor(plugin, homeKitAccessory) { 21 | super(plugin, homeKitAccessory); 22 | 23 | this.getCharacteristic(Characteristic.On).on("set", this.setOn.bind(this)); 24 | this.getCharacteristic(Characteristic.Brightness).on( 25 | "set", 26 | this.setBrightness.bind(this) 27 | ); 28 | this.getCharacteristic(Characteristic.ColorTemperature).on( 29 | "set", 30 | this.setColorTemperature.bind(this) 31 | ); 32 | this.getCharacteristic(Characteristic.Hue).on( 33 | "set", 34 | this.setHue.bind(this) 35 | ); 36 | this.getCharacteristic(Characteristic.Saturation).on( 37 | "set", 38 | this.setSaturation.bind(this) 39 | ); 40 | 41 | // Local caching of HSV color space handling separate Hue & Saturation on HomeKit 42 | // Caching idea for handling HSV colors from: 43 | // https://github.com/QuickSander/homebridge-http-rgb-push/blob/master/index.js 44 | this.cache = {}; 45 | this.cacheUpdated = false; 46 | } 47 | 48 | async updateCharacteristics(device) { 49 | if (device.conn_state == 0) { 50 | this.getCharacteristic(Characteristic.On).updateValue(noResponse); 51 | } else { 52 | this.getCharacteristic(Characteristic.On).updateValue( 53 | device.device_params.switch_state 54 | ); 55 | 56 | const propertyList = await this.plugin.client.getDevicePID( 57 | this.mac, 58 | this.product_model 59 | ); 60 | for (const property of propertyList.data.property_list) { 61 | switch (property.pid) { 62 | case WYZE_API_BRIGHTNESS_PROPERTY: 63 | if (this.isValidProperty(property)) this.updateBrightness(property.value); 64 | break; 65 | case WYZE_API_COLOR_TEMP_PROPERTY: 66 | if (this.isValidProperty(property)) this.updateColorTemp(property.value); 67 | break; 68 | case WYZE_API_COLOR_PROPERTY: 69 | if (this.isValidProperty(property)) this.updateColor(property.value); 70 | break; 71 | } 72 | } 73 | } 74 | } 75 | 76 | isValidProperty(property) { 77 | if ( 78 | property.value != null && 79 | property.value !== "0" && 80 | property.value !== "undefi" 81 | ) { 82 | return true; 83 | } else { 84 | this.plugin.log(`Encountered invalid property value: ${JSON.stringify(property, null, 2)}`); 85 | return false; 86 | } 87 | } 88 | 89 | updateBrightness(value) { 90 | if (this.plugin.config.pluginLoggingEnabled) 91 | this.plugin.log( 92 | `[MeshLight] Updating brightness record for "${this.display_name} (${ 93 | this.mac 94 | }) to ${value}: ${JSON.stringify(value)}"` 95 | ); 96 | this.getCharacteristic(Characteristic.Brightness).updateValue( 97 | this.plugin.client.checkBrightnessValue(value) 98 | ); 99 | } 100 | 101 | updateColorTemp(value) { 102 | if (this.plugin.config.pluginLoggingEnabled) 103 | this.plugin.log( 104 | `[MeshLight] Updating color Temp record for "${this.display_name} (${ 105 | this.mac 106 | }) to ${value}: ${JSON.stringify( 107 | this.plugin.client.kelvinToMired(value) 108 | )}"` 109 | ); 110 | this.getCharacteristic(Characteristic.ColorTemperature).updateValue( 111 | this.plugin.client.checkColorTemp(this.plugin.client.kelvinToMired(value)) 112 | ); 113 | } 114 | 115 | updateColor(value) { 116 | // Convert a Hex color from Wyze into the HSL values recognized by HomeKit. 117 | const hslValue = colorsys.hex2Hsv(value); 118 | if (this.plugin.config.pluginLoggingEnabled) 119 | this.plugin.log( 120 | `[MeshLight] Updating color record for "${this.display_name} (${ 121 | this.mac 122 | }) to ${value}: ${JSON.stringify(hslValue)}"` 123 | ); 124 | 125 | // Update Hue 126 | this.updateHue(hslValue.h); 127 | this.cache.hue = hslValue.h; 128 | 129 | // Update Saturation 130 | this.updateSaturation(hslValue.s); 131 | this.cache.saturation = hslValue.s; 132 | } 133 | 134 | updateHue(value) { 135 | this.getCharacteristic(Characteristic.Hue).updateValue(value); 136 | } 137 | 138 | updateSaturation(value) { 139 | this.getCharacteristic(Characteristic.Saturation).updateValue(value); 140 | } 141 | 142 | getService() { 143 | let service = this.homeKitAccessory.getService(Service.Lightbulb); 144 | 145 | if (!service) { 146 | service = this.homeKitAccessory.addService(Service.Lightbulb); 147 | } 148 | 149 | return service; 150 | } 151 | 152 | getCharacteristic(characteristic) { 153 | return this.getService().getCharacteristic(characteristic); 154 | } 155 | 156 | async setOn(value, callback) { 157 | if (this.plugin.config.pluginLoggingEnabled) 158 | this.plugin.log( 159 | `[MeshLight] Setting power for "${this.display_name} (${this.mac})" to ${value}"` 160 | ); 161 | 162 | try { 163 | await this.plugin.client.lightMeshPower( 164 | this.mac, 165 | this.product_model, 166 | value ? "1" : "0" 167 | ); 168 | callback(); 169 | } catch (e) { 170 | callback(e); 171 | } 172 | } 173 | 174 | async setBrightness(value, callback) { 175 | if (this.plugin.config.pluginLoggingEnabled) 176 | this.plugin.log( 177 | `[MeshLight] Setting brightness for "${this.display_name} (${this.mac}) to ${value}"` 178 | ); 179 | 180 | try { 181 | await this.plugin.client.setMeshBrightness( 182 | this.mac, 183 | this.product_model, 184 | value 185 | ); 186 | callback(); 187 | } catch (e) { 188 | callback(e); 189 | } 190 | } 191 | 192 | async setColorTemperature(value, callback) { 193 | if (value != null) { 194 | let floatValue = this.plugin.client.rangeToFloat( 195 | value, 196 | HOMEKIT_COLOR_TEMP_MIN, 197 | HOMEKIT_COLOR_TEMP_MAX 198 | ); 199 | let wyzeValue = this.plugin.client.floatToRange( 200 | floatValue, 201 | WYZE_COLOR_TEMP_MIN, 202 | WYZE_COLOR_TEMP_MAX 203 | ); 204 | if (this.plugin.config.pluginLoggingEnabled) 205 | this.plugin.log( 206 | `[MeshLight] Setting color temperature for "${this.display_name} (${this.mac}) to ${value} : ${wyzeValue}"` 207 | ); 208 | 209 | try { 210 | await this.plugin.client.setMeshColorTemperature( 211 | this.mac, 212 | this.product_model, 213 | wyzeValue 214 | ); 215 | callback(); 216 | } catch (e) { 217 | callback(e); 218 | } 219 | } 220 | } 221 | 222 | async setHue(value, callback) { 223 | if (value != null) { 224 | if (this.plugin.config.pluginLoggingEnabled) 225 | this.plugin.log( 226 | `[MeshLight] Setting hue (color) for "${this.display_name} (${this.mac}) to ${value} : (H)S Values: ${value}, ${this.cache.saturation}"` 227 | ); 228 | 229 | try { 230 | this.cache.hue = value; 231 | if (this.cacheUpdated) { 232 | let hexValue = colorsys.hsv2Hex( 233 | this.cache.hue, 234 | this.cache.saturation, 235 | 100 236 | ); 237 | hexValue = hexValue.replace("#", ""); 238 | if (this.plugin.config.pluginLoggingEnabled) 239 | this.plugin.log(hexValue); 240 | await this.plugin.client.setMeshHue( 241 | this.mac, 242 | this.product_model, 243 | hexValue 244 | ); 245 | this.cacheUpdated = false; 246 | } else { 247 | this.cacheUpdated = true; 248 | } 249 | callback(); 250 | } catch (e) { 251 | callback(e); 252 | } 253 | } 254 | } 255 | 256 | async setSaturation(value, callback) { 257 | if (value != null) { 258 | if (this.plugin.config.pluginLoggingEnabled) 259 | this.plugin.log( 260 | `[MeshLight] Setting saturation (color) for "${this.display_name} (${this.mac}) to ${value}"` 261 | ); 262 | if (this.plugin.config.pluginLoggingEnabled) 263 | this.plugin.log( 264 | `[MeshLight] H(S) Values: ${this.cache.saturation}, ${value}` 265 | ); 266 | 267 | try { 268 | this.cache.saturation = value; 269 | if (this.cacheUpdated) { 270 | let hexValue = colorsys.hsv2Hex( 271 | this.cache.hue, 272 | this.cache.saturation, 273 | 100 274 | ); 275 | hexValue = hexValue.replace("#", ""); 276 | await this.plugin.client.setMeshSaturation( 277 | this.mac, 278 | this.product_model, 279 | hexValue 280 | ); 281 | this.cacheUpdated = false; 282 | } else { 283 | this.cacheUpdated = true; 284 | } 285 | callback(); 286 | } catch (e) { 287 | callback(e); 288 | } 289 | } 290 | } 291 | }; 292 | -------------------------------------------------------------------------------- /src/WyzeSmartHome.js: -------------------------------------------------------------------------------- 1 | const { homebridge, Accessory, UUIDGen } = require('./types') 2 | const { OutdoorPlugModels, PlugModels, CommonModels, CameraModels, LeakSensorModels, 3 | TemperatureHumidityModels, LockModels, MotionSensorModels, ContactSensorModels, LightModels, 4 | LightStripModels, MeshLightModels, ThermostatModels, S1GatewayModels } = require('./enums') 5 | 6 | const WyzeAPI = require('wyze-api') // Uncomment for Release 7 | //const WyzeAPI = require('./wyze-api/src') // Comment for Release 8 | const WyzePlug = require('./accessories/WyzePlug') 9 | const WyzeLight = require('./accessories/WyzeLight') 10 | const WyzeMeshLight = require('./accessories/WyzeMeshLight') 11 | const WyzeLock = require('./accessories/WyzeLock') 12 | const WyzeContactSensor = require('./accessories/WyzeContactSensor') 13 | const WyzeMotionSensor = require('./accessories/WyzeMotionSensor') 14 | const WyzeTemperatureHumidity = require('./accessories/WyzeTemperatureHumidity') 15 | const WyzeLeakSensor = require('./accessories/WyzeLeakSensor') 16 | const WyzeCamera = require('./accessories/WyzeCamera') 17 | const WyzeSwitch = require('./accessories/WyzeSwitch') 18 | const WyzeHMS = require('./accessories/WyzeHMS') 19 | const WyzeThermostat = require('./accessories/WyzeThermostat') 20 | 21 | const PLUGIN_NAME = 'homebridge-wyze-smart-home' 22 | const PLATFORM_NAME = 'WyzeSmartHome' 23 | 24 | const DEFAULT_REFRESH_INTERVAL = 30000 25 | 26 | function delay(ms) { 27 | return new Promise(resolve => setTimeout(resolve, ms)) 28 | } 29 | 30 | module.exports = class WyzeSmartHome { 31 | constructor(log, config, api) { 32 | this.log = log 33 | this.config = config 34 | this.api = api 35 | this.client = this.getClient() 36 | 37 | this.accessories = [] 38 | 39 | this.api.on('didFinishLaunching', this.didFinishLaunching.bind(this)) 40 | } 41 | 42 | static register() { 43 | homebridge.registerPlatform(PLUGIN_NAME, PLATFORM_NAME, WyzeSmartHome) 44 | } 45 | 46 | getClient() { 47 | return new WyzeAPI({ 48 | // User login parameters 49 | username: this.config.username, 50 | password: this.config.password, 51 | mfaCode: this.config.mfaCode, 52 | keyId: this.config.keyId, 53 | apiKey: this.config.apiKey, 54 | //Logging 55 | apiLogEnabled: this.config.apiLogEnabled, 56 | //App Config 57 | lowBatteryPercentage: this.config.lowBatteryPercentage, 58 | //Storage Path 59 | persistPath: homebridge.user.persistPath(), 60 | //URLs 61 | authBaseUrl: this.config.authBaseUrl, 62 | apiBaseUrl: this.config.apiBaseUrl, 63 | // App emulation constants 64 | authApiKey: this.config.authApiKey, 65 | phoneId: this.config.phoneId, 66 | appName: this.config.appName, 67 | appVer: this.config.appVer, 68 | appVersion: this.config.appVersion, 69 | userAgent: this.config.userAgent, 70 | sc: this.config.sc, 71 | sv: this.config.sv, 72 | // Crypto Secrets 73 | fordAppKey: this.config.fordAppKey, // Required for Locks 74 | fordAppSecret: this.config.fordAppSecret, // Required for Locks 75 | oliveSigningSecret: this.config.oliveSigningSecret, // Required for the thermostat 76 | oliveAppId: this.config.oliveAppId, // Required for the thermostat 77 | appInfo: this.config.appInfo // Required for the thermostat 78 | }, this.log) 79 | } 80 | 81 | didFinishLaunching() { 82 | this.runLoop() 83 | } 84 | 85 | async runLoop() { 86 | const interval = this.config.refreshInterval || DEFAULT_REFRESH_INTERVAL 87 | // eslint-disable-next-line no-constant-condition 88 | while (true) { 89 | try { 90 | await this.refreshDevices() 91 | } catch (e) { } 92 | 93 | await delay(interval) 94 | } 95 | } 96 | 97 | async refreshDevices() { 98 | if (this.config.pluginLoggingEnabled) this.log('Refreshing devices...') 99 | 100 | try { 101 | const objectList = await this.client.getObjectList() 102 | const timestamp = objectList.ts 103 | const devices = objectList.data.device_list 104 | 105 | if (this.config.pluginLoggingEnabled) this.log(`Found ${devices.length} device(s)`) 106 | await this.loadDevices(devices, timestamp) 107 | } catch (e) { 108 | this.log.error(`Error getting devices: ${e}`) 109 | throw e 110 | } 111 | } 112 | 113 | async loadDevices(devices, timestamp) { 114 | const foundAccessories = [] 115 | 116 | for (const device of devices) { 117 | const accessory = await this.loadDevice(device, timestamp) 118 | if (accessory) { 119 | foundAccessories.push(accessory) 120 | } 121 | } 122 | 123 | const removedAccessories = this.accessories.filter(a => !foundAccessories.includes(a)) 124 | if (removedAccessories.length > 0) { 125 | if (this.config.pluginLoggingEnabled) this.log(`Removing ${removedAccessories.length} device(s)`) 126 | const removedHomeKitAccessories = removedAccessories.map(a => a.homeKitAccessory) 127 | this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, removedHomeKitAccessories) 128 | } 129 | 130 | this.accessories = foundAccessories 131 | } 132 | 133 | async loadDevice(device, timestamp) { 134 | const accessoryClass = this.getAccessoryClass(device.product_type, device.product_model, device.mac, device.nickname) 135 | if (!accessoryClass) { 136 | if (this.config.pluginLoggingEnabled) this.log(`[${device.product_type}] Unsupported device type: (Name: ${device.nickname}) (MAC: ${device.mac}) (Model: ${device.product_model})`) 137 | return 138 | } 139 | else if (this.config.filterByMacAddressList?.find(d => d === device.mac) || this.config.filterDeviceTypeList?.find(d => d === device.product_type)) { 140 | if (this.config.pluginLoggingEnabled) this.log(`[${device.product_type}] Ignoring (${device.nickname}) (MAC: ${device.mac}) because it is in the Ignore Device list`) 141 | return 142 | } 143 | else if (device.product_type == 'S1Gateway' && this.config.hms == false) { 144 | if (this.config.pluginLoggingEnabled) this.log(`[${device.product_type}] Ignoring (${device.nickname}) (MAC: ${device.mac}) because it is not enabled`) 145 | return 146 | } 147 | 148 | 149 | let accessory = this.accessories.find(a => a.matches(device)) 150 | if (!accessory) { 151 | const homeKitAccessory = this.createHomeKitAccessory(device) 152 | accessory = new accessoryClass(this, homeKitAccessory) 153 | this.accessories.push(accessory) 154 | } else { 155 | if (this.config.pluginLoggingEnabled) this.log(`[${device.product_type}] Loading accessory from cache ${device.nickname} (MAC: ${device.mac})`) 156 | } 157 | accessory.update(device, timestamp) 158 | 159 | return accessory 160 | } 161 | 162 | getAccessoryClass(type, model) { 163 | switch (type) { 164 | case 'OutdoorPlug': 165 | if (Object.values(OutdoorPlugModels).includes(model)) { return WyzePlug } 166 | case 'Plug': 167 | if (Object.values(PlugModels).includes(model)) { return WyzePlug } 168 | case 'Light': 169 | if (Object.values(LightModels).includes(model)) { return WyzeLight } 170 | case 'MeshLight': 171 | if (Object.values(MeshLightModels).includes(model)) { return WyzeMeshLight } 172 | case 'LightStrip': 173 | if (Object.values(LightStripModels).includes(model)) { return WyzeMeshLight } 174 | case 'ContactSensor': 175 | if (Object.values(ContactSensorModels).includes(model)) { return WyzeContactSensor } 176 | case 'MotionSensor': 177 | if (Object.values(MotionSensorModels).includes(model)) { return WyzeMotionSensor } 178 | case 'Lock': 179 | if (Object.values(LockModels).includes(model)) { return WyzeLock } 180 | case 'TemperatureHumidity': 181 | if (Object.values(TemperatureHumidityModels).includes(model)) { return WyzeTemperatureHumidity } 182 | case 'LeakSensor': 183 | if (Object.values(LeakSensorModels).includes(model)) { return WyzeLeakSensor } 184 | case 'Camera': 185 | if (Object.values(CameraModels).includes(model)) { return WyzeCamera } 186 | case 'Common': 187 | if (Object.values(CommonModels).includes(model)) { return WyzeSwitch } 188 | case 'S1Gateway': 189 | if (Object.values(S1GatewayModels).includes(model)) { return WyzeHMS } 190 | case 'Thermostat': 191 | if (Object.values(ThermostatModels).includes(model)) { return WyzeThermostat } 192 | } 193 | } 194 | 195 | createHomeKitAccessory(device) { 196 | const uuid = UUIDGen.generate(device.mac) 197 | 198 | const homeKitAccessory = new Accessory(device.nickname, uuid) 199 | 200 | homeKitAccessory.context = { 201 | mac: device.mac, 202 | product_type: device.product_type, 203 | product_model: device.product_model, 204 | nickname: device.nickname 205 | } 206 | 207 | this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [homeKitAccessory]) 208 | return homeKitAccessory 209 | } 210 | 211 | // Homebridge calls this method on boot to reinitialize previously-discovered devices 212 | configureAccessory(homeKitAccessory) { 213 | // Make sure we haven't set up this accessory already 214 | let accessory = this.accessories.find(a => a.homeKitAccessory === homeKitAccessory) 215 | if (accessory) { 216 | return 217 | } 218 | 219 | const accessoryClass = this.getAccessoryClass(homeKitAccessory.context.product_type, homeKitAccessory.context.product_model) 220 | if (accessoryClass) { 221 | accessory = new accessoryClass(this, homeKitAccessory) 222 | this.accessories.push(accessory) 223 | } else { 224 | try { 225 | this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [homeKitAccessory]) 226 | } catch (error) { 227 | this.log.error(`Error removing accessory ${homeKitAccessory.context.nickname} (MAC: ${homeKitAccessory.context.mac}) : ${error}`) 228 | } 229 | } 230 | } 231 | } -------------------------------------------------------------------------------- /src/accessories/WyzeThermostat.js: -------------------------------------------------------------------------------- 1 | const { Service, Characteristic } = require("../types"); 2 | const WyzeAccessory = require("./WyzeAccessory"); 3 | 4 | // want to add a temperature/humidity sensor to display how long it will take for system to complete current job - prop time2temp_val 5 | // add temp humidity for real humidity as well 6 | // add switch maybe to control fan mode - switch on is fan auto and switch off is fan off 7 | // want to add temp sensors for all that are connected to thermostat as well 8 | 9 | const noResponse = new Error("No Response"); 10 | noResponse.toString = () => { 11 | return noResponse.message; 12 | }; 13 | 14 | module.exports = class WyzeThermostat extends WyzeAccessory { 15 | constructor(plugin, homeKitAccessory) { 16 | super(plugin, homeKitAccessory); 17 | 18 | // Set Defaults 19 | this.thermostatTemperature = 69.0; 20 | this.thermostatCoolSetpoint = 65.0; 21 | this.thermostatHeatSetpoint = 72.0; 22 | this.thermostatModeSys = "auto"; 23 | this.thermostatWorkingState = "idle"; 24 | this.thermostatTempUnit = "F"; 25 | 26 | this.service = this.getThermostatService(); 27 | 28 | // GET current heat/cool/off state 29 | this.service 30 | .getCharacteristic(Characteristic.CurrentHeatingCoolingState) 31 | .onGet(this.handleCurrentHeatingCoolingStateGet.bind(this)); 32 | 33 | // GET current temperature 34 | this.service 35 | .getCharacteristic(Characteristic.CurrentTemperature) 36 | .onGet(this.handleCurrentTemperatureGet.bind(this)); 37 | 38 | // Target heat/cool/off state 39 | this.service 40 | .getCharacteristic(Characteristic.TargetHeatingCoolingState) 41 | .onGet(this.handleTargetHeatingCoolingStateGet.bind(this)) 42 | .onSet(this.handleTargetHeatingCoolingStateSet.bind(this)); 43 | 44 | // Target temperature handlers - needed given that the system is not in auto 45 | this.service 46 | .getCharacteristic(Characteristic.TargetTemperature) 47 | .onGet(this.handleTargetTemperatureGet.bind(this)) 48 | .onSet(this.handleTargetTemperatureSet.bind(this)); 49 | 50 | // Cooling setpoint handlers - needed for system in Auto 51 | this.service 52 | .getCharacteristic(Characteristic.CoolingThresholdTemperature) 53 | .onGet(this.handleCoolingThresholdTemperatureGet.bind(this)) 54 | .onSet(this.handleCoolingThresholdTemperatureSet.bind(this)) 55 | 56 | // Heating setpoint handlers - needed for system in Auto 57 | // Default max value is 25, but we set it higher here for Wyze thermostats 58 | // Shoutout to @fennix for the idea here: 59 | // https://github.com/tomas-kulhanek/homebridge-fenix-v24-wifi/blob/30a016ea125a5cfd439106472c1afd11f0ad6a2f/src/platformAccessory.ts#L65 60 | this.service 61 | .getCharacteristic(Characteristic.HeatingThresholdTemperature) 62 | .onGet(this.handleHeatingThresholdTemperatureGet.bind(this)) 63 | .onSet(this.handleHeatingThresholdTemperatureSet.bind(this)) 64 | .setProps({ 65 | maxValue: 35 66 | }); 67 | 68 | // Target display unit handlers 69 | this.service 70 | .getCharacteristic(Characteristic.TemperatureDisplayUnits) 71 | .onGet(this.handleTemperatureDisplayUnitsGet.bind(this)) 72 | .onSet(this.handleTemperatureDisplayUnitsSet.bind(this)); 73 | 74 | this.updateCharacteristics(); 75 | } 76 | 77 | async handleCurrentTemperatureGet() { 78 | 79 | this.debugLog("handleCurrentTemperatureGet status of " + this.display_name + " to " + this.thermostatTemperature); 80 | return this.f2c(this.thermostatTemperature); 81 | } 82 | 83 | async handleCurrentHeatingCoolingStateGet() { 84 | return this.Wyze2HomekitWorkingStates[this.thermostatWorkingState]; 85 | } 86 | 87 | async handleTargetHeatingCoolingStateGet() { 88 | return this.Wyze2HomekitStates[this.thermostatModeSys]; 89 | } 90 | 91 | async handleTargetTemperatureGet() { 92 | 93 | this.debugLog("handleTargetTemperatureGet Target Temp: " + this.c2f(this.getTargetTemperatureForSystemState())); 94 | return this.getTargetTemperatureForSystemState(); 95 | } 96 | 97 | async handleCoolingThresholdTemperatureGet() { 98 | this.debugLog("handleCoolingThresholdTemperatureGet Cool Setpoint: " + this.thermostatCoolSetpoint); 99 | return this.f2c(this.thermostatCoolSetpoint); 100 | } 101 | 102 | async handleHeatingThresholdTemperatureGet() { 103 | this.debugLog("handleHeatingThresholdTemperatureGet Heat Setpoint: " + this.thermostatHeatSetpoint); 104 | return this.f2c(this.thermostatHeatSetpoint); 105 | } 106 | 107 | async handleTemperatureDisplayUnitsGet() { 108 | return this.Wyze2HomekitUnits[this.thermostatTempUnit]; 109 | } 110 | 111 | // SET Methods for taking data from homekit and applying it to Wyze thermostat 112 | async handleTargetHeatingCoolingStateSet(value) { 113 | let targetState = this.getKey(this.Wyze2HomekitStates, value); 114 | this.debugLog("handleTargetHeatingCoolingStateSet status of " + this.display_name + " to " + targetState); 115 | 116 | this.setHvacMode(targetState); 117 | this.service 118 | .getCharacteristic(Characteristic.TargetHeatingCoolingState) 119 | .updateValue(value); 120 | this.thermostatModeSys = targetState; 121 | 122 | this.service 123 | .getCharacteristic(Characteristic.TargetTemperature) 124 | .updateValue(this.getTargetTemperatureForSystemState()); 125 | } 126 | 127 | async handleTargetTemperatureSet(value) { 128 | let targetTemp = this.c2f(value); 129 | let currentStateNumber = this.Wyze2HomekitStates[this.thermostatModeSys]; 130 | this.debugLog("handleTargetTemperatureSet status of " + this.display_name + " to " + targetTemp + " for mode " + this.thermostatModeSys + " which is " + currentStateNumber); 131 | 132 | // switch on current heating cooling state since we are NOT in auto mode 133 | switch (currentStateNumber) { 134 | case this.Wyze2HomekitStates.auto: 135 | this.debugLog("handleTargetTemperatureSet cannot set value since system is in AUTO"); 136 | break; 137 | case this.Wyze2HomekitStates.cool: 138 | this.debugLog("handleTargetTemperatureSet for COOLING"); 139 | this.handleCoolingThresholdTemperatureSet(value); 140 | break; 141 | case this.Wyze2HomekitStates.heat: 142 | this.debugLog("handleTargetTemperatureSet for HEATING"); 143 | this.handleHeatingThresholdTemperatureSet(value); 144 | break; 145 | case this.Wyze2HomekitStates.off: 146 | this.debugLog("handleTargetTemperatureSet cannot set value since system is OFF"); 147 | break; 148 | default: 149 | this.debugLog("handleTargetTemperatureSet cannot set value since system mode is UNDEFINED"); 150 | break; 151 | } 152 | 153 | this.service 154 | .getCharacteristic(Characteristic.TargetTemperature) 155 | .updateValue(value); 156 | } 157 | 158 | async handleCoolingThresholdTemperatureSet(value) { 159 | let c = this.clamp(value, 10, 35); 160 | let val = Math.round(this.c2f(c)); 161 | this.debugLog("handleCoolingThresholdTemperatureSet status of " + this.display_name + " to " + val); 162 | 163 | this.setCoolPoint(val); 164 | this.thermostatCoolSetpoint = val; 165 | this.service 166 | .getCharacteristic(Characteristic.CoolingThresholdTemperature) 167 | .updateValue(c); 168 | } 169 | 170 | async handleHeatingThresholdTemperatureSet(value) { 171 | let c = this.clamp(value, 0, 25); 172 | let val = Math.round(this.c2f(c)); 173 | this.debugLog("handleHeatingThresholdTemperatureSet status of " + this.display_name + " to " + val); 174 | 175 | this.setHeatPoint(val); 176 | this.thermostatHeatSetpoint = val; 177 | this.service 178 | .getCharacteristic(Characteristic.HeatingThresholdTemperature) 179 | .updateValue(c); 180 | } 181 | 182 | async handleTemperatureDisplayUnitsSet(value) { 183 | this.debugLog("handleTemperatureDisplayUnitsSet status of " + this.display_name + " to " + value); 184 | this.service 185 | .getCharacteristic(Characteristic.TemperatureDisplayUnits) 186 | .updateValue(value); 187 | } 188 | 189 | // this is where we do the magic 190 | async updateCharacteristics(device) { 191 | this.debugLog("Updating status of " + this.display_name); 192 | 193 | // have to wait for this call to finish before updating or we might get null data 194 | this.thermostatGetIotProp().then(this.fillData.bind(this)); 195 | } 196 | 197 | fillData() { 198 | // This just prints new data from Wyze API 199 | this.debugLog("Temp: " + this.thermostatTemperature); 200 | this.debugLog("Target Temp: " + this.c2f(this.getTargetTemperatureForSystemState())); 201 | this.debugLog("Mode Sys: " + this.thermostatModeSys + " for " + this.Wyze2HomekitStates[this.thermostatModeSys]); 202 | this.debugLog("Working State: " + this.thermostatWorkingState + " for " + this.Wyze2HomekitWorkingStates[this.thermostatWorkingState]); 203 | this.debugLog("Cool Setpoint: " + this.thermostatCoolSetpoint); 204 | this.debugLog("Heat Setpoint: " + this.thermostatHeatSetpoint); 205 | this.debugLog("Temp Units: " + this.Wyze2HomekitUnits[this.thermostatTempUnit]); 206 | } 207 | 208 | getTargetTemperatureForSystemState() { 209 | // relies on temperature setpoints to tell Homekit what the system is currently doing 210 | // Wyze seems to not actually send the "working state" to user at the moment so we have to decide what the system is doing manually 211 | 212 | let s = this.Wyze2HomekitStates[this.thermostatModeSys]; 213 | if (s == this.Wyze2HomekitStates.cool) { 214 | return this.f2c(this.thermostatCoolSetpoint); 215 | } else if (s == this.Wyze2HomekitStates.heat) { 216 | return this.f2c(this.thermostatHeatSetpoint); 217 | } else if (s == this.Wyze2HomekitStates.auto) { 218 | if (this.thermostatCoolSetpoint < this.thermostatTemperature) { 219 | return this.f2c(this.thermostatCoolSetpoint); 220 | } else if (this.thermostatHeatSetpoint > this.thermostatTemperature) { 221 | return this.f2c(this.thermostatHeatSetpoint); 222 | } else { 223 | return this.f2c(this.thermostatTemperature); 224 | } 225 | } else { 226 | return this.f2c(this.thermostatTemperature); 227 | } 228 | } 229 | 230 | getThermostatService() { 231 | this.debugLog("Retrieving previous service for " + this.display_name); 232 | let service = this.homeKitAccessory.getService(Service.Thermostat); 233 | 234 | if (!service) { 235 | this.debugLog("Adding service for " + this.display_name); 236 | service = this.homeKitAccessory.addService(Service.Thermostat); 237 | } 238 | 239 | return service; 240 | } 241 | 242 | // Wyze API Calls to GET info 243 | // Thermostat Methods 244 | async thermostatGetIotProp() { 245 | 246 | let response; 247 | try { 248 | response = await this.plugin.client.thermostatGetIotProp(this.mac); 249 | let properties = response.data.props; 250 | const prop_key = Object.keys(properties); 251 | for (const element of prop_key) { 252 | const prop = element; 253 | switch (prop) { 254 | case "temperature": 255 | this.thermostatTemperature = Math.round(properties[prop]); 256 | continue; 257 | case "cool_sp": 258 | this.thermostatCoolSetpoint = Math.round(properties[prop]); 259 | continue; 260 | case "heat_sp": 261 | this.thermostatHeatSetpoint = Math.round(properties[prop]); 262 | continue; 263 | case "working_state": 264 | this.thermostatWorkingState = properties[prop]; 265 | continue; 266 | case "temp_unit": 267 | this.thermostatTempUnit = properties[prop]; 268 | continue; 269 | case "mode_sys": 270 | this.thermostatModeSys = properties[prop]; 271 | continue; 272 | 273 | // can check for "iot_state" and "time2temp_val" in future if needed 274 | } 275 | } 276 | this.lastTimestamp = response.ts; 277 | } catch (e) { 278 | this.plugin.log.error("Error in thermostat: " + e); 279 | } finally { 280 | return response; 281 | } 282 | } 283 | 284 | // IOT API Calls 285 | 286 | async setPreset(value) { 287 | const response = await this.plugin.client.thermostatSetIotProp( 288 | this.mac, 289 | this.product_model, 290 | "config_scenario", 291 | value 292 | ); 293 | return response; 294 | } 295 | // auto, on, off / / ['auto', 'circ', 'on'] 296 | async setFanMode(value) { 297 | const response = await this.plugin.client.thermostatSetIotProp( 298 | this.mac, 299 | this.product_model, 300 | "fan_mode", 301 | value 302 | ); 303 | return response; 304 | } 305 | // auto, heat, cool 306 | async setHvacMode(value) { 307 | const response = await this.plugin.client.thermostatSetIotProp( 308 | this.mac, 309 | this.product_model, 310 | "mode_sys", 311 | value 312 | ); 313 | return response; 314 | } 315 | 316 | // heat stop point 317 | async setHeatPoint(value) { 318 | const response = await this.plugin.client.thermostatSetIotProp( 319 | this.mac, 320 | this.product_model, 321 | "heat_sp", 322 | value 323 | ); 324 | return response; 325 | } 326 | 327 | // Cool stop point 328 | async setCoolPoint(value) { 329 | const response = await this.plugin.client.thermostatSetIotProp( 330 | this.mac, 331 | this.product_model, 332 | "cool_sp", 333 | value 334 | ); 335 | return response; 336 | } 337 | 338 | // Helper Functions 339 | 340 | debugLog(message) { 341 | if (this.plugin.config.logLevel == "debug" && this.plugin.config.pluginLoggingEnabled) 342 | this.plugin.log.info( 343 | this.colors.CYAN + "[Thermostat] " + message + this.colors.RESET 344 | ); 345 | } 346 | 347 | f2c(fahrenheit) { 348 | let out = (fahrenheit - 32.0) / 1.8; 349 | return out; 350 | } 351 | 352 | c2f(celsius) { 353 | return celsius * 1.8 + 32.0; 354 | } 355 | 356 | getKey(object, value) { 357 | return Object.keys(object)[value]; 358 | } 359 | 360 | Wyze2HomekitUnits = { 361 | C: 0, 362 | F: 1, 363 | }; 364 | 365 | Wyze2HomekitStates = { 366 | off: 0, 367 | heat: 1, 368 | cool: 2, 369 | auto: 3, 370 | }; 371 | 372 | Wyze2HomekitWorkingStates = { 373 | idle: 0, 374 | heating: 1, 375 | cooling: 2, 376 | }; 377 | 378 | HomekitTargetTempValues = { 379 | max: 38, 380 | min: 10, 381 | step: 0.1 382 | } 383 | 384 | HomekitHeatingThresholdValues = { 385 | max: 25, 386 | min: 0, 387 | step: 0.1 388 | } 389 | 390 | HomekitCoolingThresholdValues = { 391 | max: 35, 392 | min: 10, 393 | step: 0.1 394 | } 395 | 396 | clamp(number, min, max) { 397 | if (number < min) { 398 | if (this.plugin.config.pluginLoggingEnabled) 399 | this.plugin.log( 400 | `[Thermostat] Clamping value: ${number} to min ${min}` 401 | ); 402 | } 403 | 404 | if (number > max) { 405 | if (this.plugin.config.pluginLoggingEnabled) 406 | this.plugin.log( 407 | `[Thermostat] Clamping value: ${number} to max ${max}` 408 | ); 409 | } 410 | 411 | return Math.max(min, Math.min(number, max)); 412 | } 413 | 414 | colors = { 415 | RESET: '\x1b[0m', 416 | BRIGHT: '\x1b[1m', 417 | DIM: '\x1b[2m', 418 | UNDERSCORE: '\x1b[4m', 419 | BLINK: '\x1b[5m', 420 | REVERSE: '\x1b[7m', 421 | HIDDEN: '\x1b[8m', 422 | BLACK: '\x1b[30m', 423 | RED: '\x1b[31m', 424 | GREEN: '\x1b[32m', 425 | YELLOW: '\x1b[33m', 426 | BLUE: '\x1b[34m', 427 | MAGENTA: '\x1b[35m', 428 | CYAN: '\x1b[36m', 429 | LIGHT_GREY: '\x1b[37m', 430 | GREY: '\x1b[90m', 431 | WHITE: '\x1b[97m' 432 | }; 433 | }; 434 | -------------------------------------------------------------------------------- /src/accessories/WyzeCamera.js: -------------------------------------------------------------------------------- 1 | const { Service, Characteristic } = require("../types"); 2 | const WyzeAccessory = require("./WyzeAccessory"); 3 | const enums = require("../enums"); 4 | 5 | const noResponse = new Error("No Response"); 6 | noResponse.toString = () => { 7 | return noResponse.message; 8 | }; 9 | 10 | module.exports = class WyzeCamera extends WyzeAccessory { 11 | constructor(plugin, homeKitAccessory) { 12 | super(plugin, homeKitAccessory); 13 | 14 | if (Object.values(enums.CameraModels).includes(this.product_model)) { 15 | if (this.plugin.config.pluginLoggingEnabled) 16 | this.plugin.log( 17 | `[Camera] [Privacy Switch] Retrieving previous service for ${this.mac} (${this.display_name})` 18 | ); 19 | this.privacySwitch = this.homeKitAccessory.getService(this.display_name); 20 | 21 | if (!this.privacySwitch) { 22 | if (this.plugin.config.pluginLoggingEnabled) 23 | this.plugin.log( 24 | `[Camera] [Privacy Switch] Adding service for ${this.mac} (${this.display_name})` 25 | ); 26 | this.privacySwitch = this.homeKitAccessory.addService( 27 | Service.Switch, 28 | this.display_name, 29 | "Privacy" 30 | ); 31 | } 32 | 33 | this.privacySwitch 34 | .getCharacteristic(Characteristic.On) 35 | .onGet(this.handleOnGetPrivacySwitch.bind(this)) 36 | .onSet(this.handleOnSetPrivacySwitch.bind(this)); 37 | 38 | if (this.cameraAccessoryAttached()) { 39 | if ( 40 | this.plugin.config.garageDoorAccessory?.find((d) => d === this.mac) 41 | ) { 42 | this.garageDoorEnabled = true; 43 | if (this.plugin.config.pluginLoggingEnabled) 44 | this.plugin.log( 45 | `[Camera] [Garage Door] Retrieving previous service for ${this.mac} (${this.display_name})` 46 | ); 47 | this.garageDoorService = this.homeKitAccessory.getService( 48 | Service.GarageDoorOpener 49 | ); 50 | if (!this.garageDoorService) { 51 | if (this.plugin.config.pluginLoggingEnabled) 52 | this.plugin.log( 53 | `[Camera] [Garage Door] Adding service for ${this.mac} (${this.display_name})` 54 | ); 55 | this.garageDoorService = this.homeKitAccessory.addService( 56 | Service.GarageDoorOpener 57 | ); 58 | } 59 | // create handlers for required characteristics 60 | this.garageDoorService 61 | .getCharacteristic(Characteristic.CurrentDoorState) 62 | .onGet(this.getGarageCurrentState.bind(this)); 63 | 64 | this.garageDoorService 65 | .getCharacteristic(Characteristic.TargetDoorState) 66 | .onGet(this.getGarageTargetState.bind(this)) 67 | .onSet(this.setGarageTargetState.bind(this)); 68 | 69 | this.garageDoorService 70 | .getCharacteristic(Characteristic.ObstructionDetected) 71 | .onGet(this.handleObstructionDetectedGet.bind(this)); 72 | } 73 | if ( 74 | this.plugin.config.spotLightAccessory?.find((d) => d === this.mac) 75 | ) { 76 | this.spotLightEnabled = true; 77 | if (this.plugin.config.pluginLoggingEnabled) 78 | this.plugin.log( 79 | `[Camera] [Spotlight Switch] Retrieving previous service for ${this.mac} (${this.display_name})` 80 | ); 81 | 82 | this.spotLightService = this.homeKitAccessory.getService( 83 | Service.Lightbulb 84 | ); 85 | if (!this.spotLightService) { 86 | if (this.plugin.config.pluginLoggingEnabled) 87 | this.plugin.log( 88 | `[Camera] [Spotlight] Adding service for ${this.mac} (${this.display_name})` 89 | ); 90 | this.spotLightService = this.homeKitAccessory.addService( 91 | Service.Lightbulb, 92 | this.display_name + " Spotlight", 93 | "Spotlight" 94 | ); 95 | } 96 | 97 | this.spotLightService 98 | .getCharacteristic(Characteristic.On) 99 | .onGet(this.handleOnGetSpotlight.bind(this)) 100 | .onSet(this.handleOnSetSpotlight.bind(this)); 101 | } 102 | if ( 103 | this.plugin.config.floodLightAccessory?.find((d) => d === this.mac) 104 | ) { 105 | this.floodLightEnabled = true; 106 | if (this.plugin.config.pluginLoggingEnabled) 107 | this.plugin.log( 108 | `[Camera] [FloodLight] Retrieving previous service for ${this.mac} (${this.display_name})` 109 | ); 110 | 111 | this.floodLightService = this.homeKitAccessory.getService( 112 | Service.Lightbulb 113 | ); 114 | if (!this.floodLightService) { 115 | if (this.plugin.config.pluginLoggingEnabled) 116 | this.plugin.log( 117 | `[Camera] [FloodLight] Adding service for ${this.mac} (${this.display_name})` 118 | ); 119 | this.floodLightService = this.homeKitAccessory.addService( 120 | Service.Lightbulb, 121 | this.display_name + " FloodLight", 122 | "FloodLight" 123 | ); 124 | } 125 | 126 | this.floodLightService 127 | .getCharacteristic(Characteristic.On) 128 | .onGet(this.handleOnGetFloodlight.bind(this)) 129 | .onSet(this.handleOnSetFloodlight.bind(this)); 130 | } 131 | if (this.plugin.config.sirenAccessory?.find((d) => d === this.mac)) { 132 | this.sirenEnabled = true; 133 | if (this.plugin.config.pluginLoggingEnabled) 134 | this.plugin.log( 135 | `[Camera] [Siren] Retrieving previous service for ${this.mac} (${this.display_name})` 136 | ); 137 | this.sirenSwitch = this.homeKitAccessory.getService( 138 | this.display_name + " Siren" 139 | ); 140 | if (!this.sirenSwitch) { 141 | if (this.plugin.config.pluginLoggingEnabled) 142 | this.plugin.log( 143 | `[Camera] [Alarm Switch] Adding service for ${this.mac} (${this.display_name})` 144 | ); 145 | this.sirenSwitch = this.homeKitAccessory.addService( 146 | Service.Switch, 147 | this.display_name + " Siren", 148 | "Siren" 149 | ); 150 | } 151 | 152 | this.sirenSwitch 153 | .getCharacteristic(Characteristic.On) 154 | .onGet(this.handleOnGetAlarmSwitch.bind(this)) 155 | .onSet(this.handleOnSetAlarmSwitch.bind(this)); 156 | } 157 | if ( 158 | this.plugin.config.notificationAccessory?.find((d) => d === this.mac) 159 | ) { 160 | if (this.plugin.config.pluginLoggingEnabled) 161 | this.plugin.log( 162 | `[Camera] [Notification] Retrieving previous service for ${this.mac} (${this.display_name})` 163 | ); 164 | this.notificationSwitch = this.homeKitAccessory.getService( 165 | this.display_name + " Notification" 166 | ); 167 | if (!this.notificationSwitch) { 168 | if (this.plugin.config.pluginLoggingEnabled) 169 | this.plugin.log( 170 | `[Camera] [Notification] Adding service for ${this.mac} (${this.display_name})` 171 | ); 172 | this.notificationSwitch = this.homeKitAccessory.addService( 173 | Service.Switch, 174 | this.display_name + " Notification", 175 | "Notification" 176 | ); 177 | } 178 | 179 | this.notificationSwitch 180 | .getCharacteristic(Characteristic.On) 181 | .onGet(this.getNotification.bind(this)) 182 | .onSet(this.setNotification.bind(this)); 183 | } 184 | } 185 | } 186 | } 187 | 188 | async updateCharacteristics(device) { 189 | if (device.conn_state === 0) { 190 | if (this.plugin.config.pluginLoggingEnabled) 191 | this.plugin.log( 192 | `[Camera] Updating status ${this.mac} (${this.display_name}) to noResponse` 193 | ); 194 | this.privacySwitch 195 | .getCharacteristic(Characteristic.On) 196 | .updateValue(noResponse); 197 | if (this.plugin.config.sirenAccessory?.find((d) => d === device.mac)) { 198 | if (this.plugin.config.pluginLoggingEnabled) 199 | this.plugin.log( 200 | `[Camera] [Siren] Updating status ${this.mac} (${this.display_name}) to noResponse` 201 | ); 202 | this.sirenSwitch 203 | .getCharacteristic(Characteristic.On) 204 | .updateValue(noResponse); 205 | } 206 | if (this.plugin.config.floodLightAccessory?.find((d) => d === this.mac)) { 207 | if (this.plugin.config.pluginLoggingEnabled) 208 | this.plugin.log( 209 | `[Camera] [FloodLight] Updating status of ${this.mac} (${this.display_name}) to noResponse` 210 | ); 211 | this.floodLightService 212 | .getCharacteristic(Characteristic.On) 213 | .updateValue(noResponse); 214 | } 215 | if (this.plugin.config.spotLightAccessory?.find((d) => d === this.mac)) { 216 | if (this.plugin.config.pluginLoggingEnabled) 217 | this.plugin.log( 218 | `[Camera] [SpotLight] Updating status of ${this.mac} (${this.display_name}) to noResponse` 219 | ); 220 | this.spotLightService 221 | .getCharacteristic(Characteristic.On) 222 | .updateValue(noResponse); 223 | } 224 | if (this.plugin.config.garageDoorAccessory?.find((d) => d === this.mac)) { 225 | if (this.plugin.config.pluginLoggingEnabled) 226 | this.plugin.log( 227 | `[Camera] [Garage Door] Updating status of ${this.mac} (${this.display_name}) to noResponse` 228 | ); 229 | this.garageDoorService 230 | .getCharacteristic(Characteristic.CurrentDoorState) 231 | .updateValue(noResponse); 232 | } 233 | if ( 234 | this.plugin.config.notificationAccessory?.find((d) => d === this.mac) 235 | ) { 236 | if (this.plugin.config.pluginLoggingEnabled) 237 | this.plugin.log( 238 | `[Camera] [Notification] Updating status of ${this.mac} (${this.display_name}) to noResponse` 239 | ); 240 | this.notificationSwitch 241 | .getCharacteristic(Characteristic.On) 242 | .updateValue(noResponse); 243 | } 244 | } else { 245 | if (this.cameraAccessoryAttached()) { 246 | const propertyList = await this.plugin.client.getDevicePID( 247 | this.mac, 248 | this.product_model 249 | ); 250 | for (const property of propertyList.data.property_list) { 251 | switch (property.pid) { 252 | case "P1": 253 | if ( 254 | this.plugin.config.notificationAccessory?.find( 255 | (d) => d === this.mac 256 | ) 257 | ) { 258 | if (this.plugin.config.pluginLoggingEnabled) { 259 | this.plugin.log( 260 | `[Camera] [Notification] Updating status of ${this.mac} (${this.display_name})` 261 | ); 262 | } 263 | this.notification = property.value; 264 | this.notificationSwitch 265 | .getCharacteristic(Characteristic.On) 266 | .updateValue(this.notification); 267 | } 268 | break; 269 | case "P3": 270 | if (this.plugin.config.pluginLoggingEnabled) 271 | this.plugin.log( 272 | `[Camera] [Privacy] Updating status of ${this.mac} (${this.display_name})` 273 | ); 274 | this.on = property.value; 275 | this.privacySwitch 276 | .getCharacteristic(Characteristic.On) 277 | .updateValue(this.on); 278 | break; 279 | case "P5": 280 | this.available = property.value; 281 | break; 282 | case "P1049": 283 | if ( 284 | this.plugin.config.sirenAccessory?.find((d) => d === this.mac) 285 | ) { 286 | if (this.plugin.config.pluginLoggingEnabled) { 287 | this.plugin.log( 288 | `[Camera] [Siren] Updating status of ${this.mac} (${this.display_name})` 289 | ); 290 | } 291 | this.siren = property.value; 292 | this.sirenSwitch 293 | .getCharacteristic(Characteristic.On) 294 | .updateValue(this.siren); 295 | } 296 | break; 297 | case "P1056": 298 | if ( 299 | this.plugin.config.spotLightAccessory?.find( 300 | (d) => d === this.mac 301 | ) 302 | ) { 303 | if (this.plugin.config.pluginLoggingEnabled) { 304 | this.plugin.log( 305 | `[Camera] [SpotLight] Updating status of ${this.mac} (${this.display_name})` 306 | ); 307 | } 308 | this.floodLight = property.value; 309 | this.spotLightService 310 | .getCharacteristic(Characteristic.On) 311 | .updateValue(this.floodLight); 312 | } 313 | break; 314 | case "P1301": 315 | if ( 316 | this.plugin.config.garageDoorAccessory?.find( 317 | (d) => d === this.mac 318 | ) 319 | ) { 320 | if (this.plugin.config.pluginLoggingEnabled) { 321 | this.plugin.log( 322 | `[Camera] [Garage Door] Updating status of ${this.mac} (${this.display_name})` 323 | ); 324 | } 325 | this.garageDoor = property.value; 326 | } 327 | break; 328 | } 329 | } 330 | } else { 331 | if (this.plugin.config.pluginLoggingEnabled) 332 | this.plugin.log( 333 | `[Camera] [Privacy] Updating status of ${this.mac} (${this.display_name})` 334 | ); 335 | this.power_switch = device.device_params.power_switch; 336 | this.privacySwitch 337 | .getCharacteristic(Characteristic.On) 338 | .updateValue(device.device_params.power_switch); 339 | } 340 | } 341 | } 342 | 343 | async getGarageCurrentState() { 344 | if (this.plugin.config.pluginLoggingEnabled) 345 | this.plugin.log( 346 | `[Camera Garage Door] Getting Current State for ${this.mac} (${this.display_name} : ${this.garageDoor})` 347 | ); 348 | let currentValue; 349 | 350 | if (this.garageDoor == 1) { 351 | currentValue = Characteristic.CurrentDoorState.OPEN; 352 | } else currentValue = Characteristic.CurrentDoorState.CLOSED; 353 | return currentValue; 354 | } 355 | 356 | async getGarageTargetState() { 357 | if (this.plugin.config.pluginLoggingEnabled) 358 | this.plugin.log( 359 | `[Camera Garage Door] Getting Target State for ${this.mac} (${this.display_name} : ${this.garageDoor})` 360 | ); 361 | 362 | let currentValue; 363 | 364 | if (this.garageDoor == 1) { 365 | currentValue = Characteristic.TargetDoorState.OPEN; 366 | } else currentValue = Characteristic.TargetDoorState.CLOSED; 367 | 368 | return currentValue; 369 | } 370 | 371 | async handleObstructionDetectedGet() { 372 | if (this.plugin.config.pluginLoggingEnabled) 373 | this.plugin.log( 374 | `[Camera Garage Door] Getting ObstructionState for ${this.mac} (${this.display_name})` 375 | ); 376 | 377 | return 0; 378 | } 379 | 380 | async handleOnGetPrivacySwitch() { 381 | if (this.cameraAccessoryAttached()) { 382 | this.powerSwitch = this.on; 383 | } else this.powerSwitch = this.power_switch; 384 | if (this.plugin.config.pluginLoggingEnabled) 385 | this.plugin.log( 386 | `[Camera] [Privacy] Getting Current State for ${this.mac} (${this.display_name} : ${this.powerSwitch})` 387 | ); 388 | if (this.powerSwitch === "undefined" || this.powerSwitch == null) { 389 | return 0; 390 | } else { 391 | return this.powerSwitch; 392 | } 393 | } 394 | 395 | async handleOnGetSpotlight() { 396 | if (this.plugin.config.pluginLoggingEnabled) 397 | this.plugin.log( 398 | `[Camera] [SpotLight] Getting Current State for ${this.mac} (${this.display_name} : ${this.floodLight})` 399 | ); 400 | if (this.floodLight === "undefined" || this.floodLight == null) { 401 | return 0; 402 | } else return this.floodLight; 403 | } 404 | 405 | async handleOnGetFloodlight() { 406 | if (this.plugin.config.pluginLoggingEnabled) 407 | this.plugin.log( 408 | `[Camera] [FloodLight] Getting Current State for ${this.mac} (${this.display_name} : ${this.floodLight})` 409 | ); 410 | if (this.floodLight === "undefined" || this.floodLight == null) { 411 | return 0; 412 | } else return this.floodLight; 413 | } 414 | 415 | async handleOnGetAlarmSwitch() { 416 | if (this.plugin.config.pluginLoggingEnabled) 417 | this.plugin.log( 418 | `[Camera] [Siren] Getting Current State for ${this.mac} (${this.display_name} : ${this.siren})` 419 | ); 420 | if (this.siren === "undefined" || this.siren == null) { 421 | return 0; 422 | } else return this.siren; 423 | } 424 | 425 | async getNotification() { 426 | if (this.plugin.config.pluginLoggingEnabled) 427 | this.plugin.log( 428 | `[Camera] [Notification] Getting Current State for ${this.mac} (${this.display_name} : ${this.notification})` 429 | ); 430 | if (this.notification === "undefined" || this.notification == null) { 431 | return 0; 432 | } else return this.notification; 433 | } 434 | 435 | async handleOnSetSpotlight(value) { 436 | if (this.plugin.config.pluginLoggingEnabled) 437 | this.plugin.log( 438 | `[Camera] [SpotLight] Setting Current State for ${this.mac} (${this.display_name}) to ${value}` 439 | ); 440 | this.plugin.client.cameraSpotLight( 441 | this.mac, 442 | this.product_model, 443 | value ? "1" : "2" 444 | ); 445 | } 446 | 447 | async handleOnSetFloodlight(value) { 448 | if (this.plugin.config.pluginLoggingEnabled) 449 | this.plugin.log( 450 | `[Camera] [FloodLight] Setting Current State for ${this.mac} (${this.display_name}) to ${value}` 451 | ); 452 | this.plugin.client.cameraFloodLight( 453 | this.mac, 454 | this.product_model, 455 | value ? "1" : "2" 456 | ); 457 | } 458 | 459 | async handleOnSetPrivacySwitch(value) { 460 | if (this.plugin.config.pluginLoggingEnabled) 461 | this.plugin.log( 462 | `[Camera] [Privacy] Setting Current State for ${this.mac} (${this.display_name}) to ${value}` 463 | ); 464 | this.plugin.client.cameraPrivacy( 465 | this.mac, 466 | this.product_model, 467 | value ? "power_on" : "power_off" 468 | ); 469 | } 470 | 471 | async handleOnSetAlarmSwitch(value) { 472 | if (this.plugin.config.pluginLoggingEnabled) 473 | this.plugin.log( 474 | `[Camera] [Siren] Setting Current State for ${this.mac} (${this.display_name}) to ${value}` 475 | ); 476 | this.plugin.client.cameraSiren( 477 | this.mac, 478 | this.product_model, 479 | value ? "siren_on" : "siren_off" 480 | ); 481 | } 482 | 483 | async setNotification(value) { 484 | if (this.plugin.config.pluginLoggingEnabled) 485 | this.plugin.log( 486 | `[Camera] [Notification] Setting Current State for ${this.mac} (${this.display_name}) to ${value}` 487 | ); 488 | this.plugin.client.cameraNotifications( 489 | this.mac, 490 | this.product_model, 491 | value ? "1" : "0" 492 | ); 493 | } 494 | 495 | async setGarageTargetState(value) { 496 | if (this.plugin.config.pluginLoggingEnabled) 497 | this.plugin.log( 498 | `[Camera Garage Door] Setting Target State for ${this.mac} (${this.display_name}) to ${value}` 499 | ); 500 | this.plugin.client.garageDoor(this.mac, this.product_model); 501 | if (value == 0) { 502 | this.garageDoorService 503 | .getCharacteristic(Characteristic.CurrentDoorState) 504 | .updateValue(Characteristic.CurrentDoorState.OPEN); 505 | } else if (value == 1) { 506 | this.garageDoorService 507 | .getCharacteristic(Characteristic.CurrentDoorState) 508 | .updateValue(Characteristic.CurrentDoorState.CLOSED); 509 | } 510 | } 511 | 512 | cameraAccessoryAttached() { 513 | return !!( 514 | this.plugin.config.garageDoorAccessory?.find((d) => d === this.mac) || 515 | this.plugin.config.spotLightAccessory?.find((d) => d === this.mac) || 516 | this.plugin.config.alarmAccessory?.find((d) => d === this.mac) || 517 | this.plugin.config.floodLightAccessory?.find((d) => d === this.mac) || 518 | this.plugin.config.notificationAccessory?.find((d) => d === this.mac) 519 | ); 520 | } 521 | }; 522 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@aashutoshrathi/word-wrap@^1.2.3": 6 | version "1.2.6" 7 | resolved "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz" 8 | integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== 9 | 10 | "@eslint-community/eslint-utils@^4.2.0": 11 | version "4.4.0" 12 | resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" 13 | integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== 14 | dependencies: 15 | eslint-visitor-keys "^3.3.0" 16 | 17 | "@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": 18 | version "4.10.0" 19 | resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz" 20 | integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== 21 | 22 | "@eslint/eslintrc@^2.1.4": 23 | version "2.1.4" 24 | resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz" 25 | integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== 26 | dependencies: 27 | ajv "^6.12.4" 28 | debug "^4.3.2" 29 | espree "^9.6.0" 30 | globals "^13.19.0" 31 | ignore "^5.2.0" 32 | import-fresh "^3.2.1" 33 | js-yaml "^4.1.0" 34 | minimatch "^3.1.2" 35 | strip-json-comments "^3.1.1" 36 | 37 | "@eslint/js@8.56.0": 38 | version "8.56.0" 39 | resolved "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz" 40 | integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A== 41 | 42 | "@fastify/busboy@^2.0.0": 43 | version "2.1.0" 44 | resolved "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz" 45 | integrity sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA== 46 | 47 | "@homebridge/ciao@^1.1.5": 48 | version "1.1.8" 49 | resolved "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.1.8.tgz" 50 | integrity sha512-Atn8+vwYtfI/J6nYCOVm4uVBAmiQO4rPi0umVbh766cf/OsVxQ+Qedbo9lxIf15iDsMbBlDV7T1wATdHqI5lXw== 51 | dependencies: 52 | debug "^4.3.4" 53 | fast-deep-equal "^3.1.3" 54 | source-map-support "^0.5.21" 55 | tslib "^2.6.2" 56 | 57 | "@homebridge/dbus-native@^0.5.1": 58 | version "0.5.1" 59 | resolved "https://registry.npmjs.org/@homebridge/dbus-native/-/dbus-native-0.5.1.tgz" 60 | integrity sha512-7xXz3R1W/kcbfQOGp32y4K7etqtowICR1vpx8j85KwPYXbNQrgiZ3zcwDYgDGBWq3FD9xzsW7h4YWJ4vTR2seQ== 61 | dependencies: 62 | "@homebridge/long" "^5.2.1" 63 | "@homebridge/put" "~0.0.8" 64 | event-stream "^4.0.0" 65 | hexy "^0.2.10" 66 | minimist "^1.2.6" 67 | safe-buffer "^5.1.1" 68 | xml2js "^0.5.0" 69 | 70 | "@homebridge/long@^5.2.1": 71 | version "5.2.1" 72 | resolved "https://registry.npmjs.org/@homebridge/long/-/long-5.2.1.tgz" 73 | integrity sha512-i5Df8R63XNPCn+Nj1OgAoRdw9e+jHUQb3CNUbvJneI2iu3j4+OtzQj+5PA1Ce+747NR1SPqZSvyvD483dOT3AA== 74 | 75 | "@homebridge/put@~0.0.8": 76 | version "0.0.8" 77 | resolved "https://registry.npmjs.org/@homebridge/put/-/put-0.0.8.tgz" 78 | integrity sha512-mwxLHHqKebOmOSU0tsPEWQSBHGApPhuaqtNpCe7U+AMdsduweANiu64E9SXXUtdpyTjsOpgSMLhD1+kbLHD2gA== 79 | 80 | "@humanwhocodes/config-array@^0.11.13": 81 | version "0.11.14" 82 | resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz" 83 | integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== 84 | dependencies: 85 | "@humanwhocodes/object-schema" "^2.0.2" 86 | debug "^4.3.1" 87 | minimatch "^3.0.5" 88 | 89 | "@humanwhocodes/module-importer@^1.0.1": 90 | version "1.0.1" 91 | resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" 92 | integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== 93 | 94 | "@humanwhocodes/object-schema@^2.0.2": 95 | version "2.0.2" 96 | resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz" 97 | integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== 98 | 99 | "@leichtgewicht/ip-codec@^2.0.1": 100 | version "2.0.4" 101 | resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz" 102 | integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== 103 | 104 | "@nodelib/fs.scandir@2.1.5": 105 | version "2.1.5" 106 | resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" 107 | integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== 108 | dependencies: 109 | "@nodelib/fs.stat" "2.0.5" 110 | run-parallel "^1.1.9" 111 | 112 | "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": 113 | version "2.0.5" 114 | resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" 115 | integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== 116 | 117 | "@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": 118 | version "1.2.8" 119 | resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" 120 | integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== 121 | dependencies: 122 | "@nodelib/fs.scandir" "2.1.5" 123 | fastq "^1.6.0" 124 | 125 | "@ptkdev/logger@1.8.0": 126 | version "1.8.0" 127 | resolved "https://registry.npmjs.org/@ptkdev/logger/-/logger-1.8.0.tgz" 128 | integrity sha512-gwg0pleMUyzsZIErDtzz2OP4F2Q3nzRGjhUTP+831/eogq17LpG7PgbHo1n1HZ/dlz/v2xvotfUcPLO3IzwEVQ== 129 | dependencies: 130 | chalk "^4.1.2" 131 | fs-extra "^10.0.0" 132 | lowdb "^1.0.0" 133 | rotating-file-stream "^2.1.5" 134 | strip-ansi "^6.0.0" 135 | 136 | "@types/json-schema@^7.0.9": 137 | version "7.0.15" 138 | resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" 139 | integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== 140 | 141 | "@types/semver@^7.3.12": 142 | version "7.5.6" 143 | resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz" 144 | integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== 145 | 146 | "@typescript-eslint/eslint-plugin@^5.27.1": 147 | version "5.62.0" 148 | resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz" 149 | integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== 150 | dependencies: 151 | "@eslint-community/regexpp" "^4.4.0" 152 | "@typescript-eslint/scope-manager" "5.62.0" 153 | "@typescript-eslint/type-utils" "5.62.0" 154 | "@typescript-eslint/utils" "5.62.0" 155 | debug "^4.3.4" 156 | graphemer "^1.4.0" 157 | ignore "^5.2.0" 158 | natural-compare-lite "^1.4.0" 159 | semver "^7.3.7" 160 | tsutils "^3.21.0" 161 | 162 | "@typescript-eslint/parser@^5.27.1": 163 | version "5.62.0" 164 | resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz" 165 | integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== 166 | dependencies: 167 | "@typescript-eslint/scope-manager" "5.62.0" 168 | "@typescript-eslint/types" "5.62.0" 169 | "@typescript-eslint/typescript-estree" "5.62.0" 170 | debug "^4.3.4" 171 | 172 | "@typescript-eslint/scope-manager@5.62.0": 173 | version "5.62.0" 174 | resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz" 175 | integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== 176 | dependencies: 177 | "@typescript-eslint/types" "5.62.0" 178 | "@typescript-eslint/visitor-keys" "5.62.0" 179 | 180 | "@typescript-eslint/type-utils@5.62.0": 181 | version "5.62.0" 182 | resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz" 183 | integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== 184 | dependencies: 185 | "@typescript-eslint/typescript-estree" "5.62.0" 186 | "@typescript-eslint/utils" "5.62.0" 187 | debug "^4.3.4" 188 | tsutils "^3.21.0" 189 | 190 | "@typescript-eslint/types@5.62.0": 191 | version "5.62.0" 192 | resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz" 193 | integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== 194 | 195 | "@typescript-eslint/typescript-estree@5.62.0": 196 | version "5.62.0" 197 | resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz" 198 | integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== 199 | dependencies: 200 | "@typescript-eslint/types" "5.62.0" 201 | "@typescript-eslint/visitor-keys" "5.62.0" 202 | debug "^4.3.4" 203 | globby "^11.1.0" 204 | is-glob "^4.0.3" 205 | semver "^7.3.7" 206 | tsutils "^3.21.0" 207 | 208 | "@typescript-eslint/utils@5.62.0": 209 | version "5.62.0" 210 | resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz" 211 | integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== 212 | dependencies: 213 | "@eslint-community/eslint-utils" "^4.2.0" 214 | "@types/json-schema" "^7.0.9" 215 | "@types/semver" "^7.3.12" 216 | "@typescript-eslint/scope-manager" "5.62.0" 217 | "@typescript-eslint/types" "5.62.0" 218 | "@typescript-eslint/typescript-estree" "5.62.0" 219 | eslint-scope "^5.1.1" 220 | semver "^7.3.7" 221 | 222 | "@typescript-eslint/visitor-keys@5.62.0": 223 | version "5.62.0" 224 | resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz" 225 | integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== 226 | dependencies: 227 | "@typescript-eslint/types" "5.62.0" 228 | eslint-visitor-keys "^3.3.0" 229 | 230 | "@ungap/structured-clone@^1.2.0": 231 | version "1.2.0" 232 | resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz" 233 | integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== 234 | 235 | acorn-jsx@^5.3.2: 236 | version "5.3.2" 237 | resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" 238 | integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== 239 | 240 | acorn@^8.9.0: 241 | version "8.11.3" 242 | resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" 243 | integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== 244 | 245 | ajv@^6.12.4: 246 | version "6.12.6" 247 | resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" 248 | integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== 249 | dependencies: 250 | fast-deep-equal "^3.1.1" 251 | fast-json-stable-stringify "^2.0.0" 252 | json-schema-traverse "^0.4.1" 253 | uri-js "^4.2.2" 254 | 255 | ansi-regex@^5.0.1: 256 | version "5.0.1" 257 | resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" 258 | integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== 259 | 260 | ansi-styles@^4.1.0: 261 | version "4.3.0" 262 | resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" 263 | integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== 264 | dependencies: 265 | color-convert "^2.0.1" 266 | 267 | argparse@^2.0.1: 268 | version "2.0.1" 269 | resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" 270 | integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== 271 | 272 | array-buffer-byte-length@^1.0.0: 273 | version "1.0.1" 274 | resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz" 275 | integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== 276 | dependencies: 277 | call-bind "^1.0.5" 278 | is-array-buffer "^3.0.4" 279 | 280 | array-flatten@^2.1.2: 281 | version "2.1.2" 282 | resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz" 283 | integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== 284 | 285 | array-union@^2.1.0: 286 | version "2.1.0" 287 | resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" 288 | integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== 289 | 290 | asynckit@^0.4.0: 291 | version "0.4.0" 292 | resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" 293 | integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== 294 | 295 | available-typed-arrays@^1.0.6: 296 | version "1.0.6" 297 | resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz" 298 | integrity sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg== 299 | 300 | axios@1.5.0: 301 | version "1.5.0" 302 | resolved "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz" 303 | integrity sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ== 304 | dependencies: 305 | follow-redirects "^1.15.0" 306 | form-data "^4.0.0" 307 | proxy-from-env "^1.1.0" 308 | 309 | axios@^1.5.0: 310 | version "1.7.4" 311 | resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.4.tgz#4c8ded1b43683c8dd362973c393f3ede24052aa2" 312 | integrity sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw== 313 | dependencies: 314 | follow-redirects "^1.15.6" 315 | form-data "^4.0.0" 316 | proxy-from-env "^1.1.0" 317 | 318 | balanced-match@^1.0.0: 319 | version "1.0.2" 320 | resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" 321 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 322 | 323 | base64-js@1.5.1: 324 | version "1.5.1" 325 | resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" 326 | integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== 327 | 328 | bonjour-hap@~3.6.4: 329 | version "3.6.4" 330 | resolved "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.6.4.tgz" 331 | integrity sha512-a76r95/qTAP5hOEZZhRoiosyFSVPPRSVev09Jh8yDf3JDKyrzELLf0vpQCuEXFueb9DcV9UJf2Jv3dktyuPBng== 332 | dependencies: 333 | array-flatten "^2.1.2" 334 | deep-equal "^2.0.5" 335 | ip "^1.1.8" 336 | multicast-dns "^7.2.5" 337 | multicast-dns-service-types "^1.1.0" 338 | 339 | brace-expansion@^1.1.7: 340 | version "1.1.11" 341 | resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" 342 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 343 | dependencies: 344 | balanced-match "^1.0.0" 345 | concat-map "0.0.1" 346 | 347 | braces@^3.0.2: 348 | version "3.0.3" 349 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" 350 | integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== 351 | dependencies: 352 | fill-range "^7.1.1" 353 | 354 | buffer-from@^1.0.0: 355 | version "1.1.2" 356 | resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" 357 | integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== 358 | 359 | call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5: 360 | version "1.0.5" 361 | resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz" 362 | integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== 363 | dependencies: 364 | function-bind "^1.1.2" 365 | get-intrinsic "^1.2.1" 366 | set-function-length "^1.1.1" 367 | 368 | callsites@^3.0.0: 369 | version "3.1.0" 370 | resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" 371 | integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== 372 | 373 | chalk@^4.0.0, chalk@^4.1.2: 374 | version "4.1.2" 375 | resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" 376 | integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== 377 | dependencies: 378 | ansi-styles "^4.1.0" 379 | supports-color "^7.1.0" 380 | 381 | charenc@0.0.2: 382 | version "0.0.2" 383 | resolved "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz" 384 | integrity sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA== 385 | 386 | color-convert@^2.0.1: 387 | version "2.0.1" 388 | resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" 389 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 390 | dependencies: 391 | color-name "~1.1.4" 392 | 393 | color-name@~1.1.4: 394 | version "1.1.4" 395 | resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" 396 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 397 | 398 | colorsys@^1.0.22: 399 | version "1.0.22" 400 | resolved "https://registry.npmjs.org/colorsys/-/colorsys-1.0.22.tgz" 401 | integrity sha512-KCqF23oqkOD0IUCTLCl0obwGIMyeGFlNWuJ4oRRVKmawvKQeb3x5UvajVeH9AShZWU9hNaIhjXeTGw3iPNtl/Q== 402 | 403 | combined-stream@^1.0.8: 404 | version "1.0.8" 405 | resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" 406 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== 407 | dependencies: 408 | delayed-stream "~1.0.0" 409 | 410 | commander@5.1.0: 411 | version "5.1.0" 412 | resolved "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz" 413 | integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== 414 | 415 | concat-map@0.0.1: 416 | version "0.0.1" 417 | resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" 418 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== 419 | 420 | cross-spawn@^7.0.2: 421 | version "7.0.3" 422 | resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" 423 | integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== 424 | dependencies: 425 | path-key "^3.1.0" 426 | shebang-command "^2.0.0" 427 | which "^2.0.1" 428 | 429 | crypt@0.0.2: 430 | version "0.0.2" 431 | resolved "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz" 432 | integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== 433 | 434 | crypto-js@4.1.1: 435 | version "4.1.1" 436 | resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz" 437 | integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== 438 | 439 | crypto-js@4.2.0: 440 | version "4.2.0" 441 | resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz" 442 | integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== 443 | 444 | debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: 445 | version "4.3.4" 446 | resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" 447 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 448 | dependencies: 449 | ms "2.1.2" 450 | 451 | deep-equal@^2.0.5: 452 | version "2.2.3" 453 | resolved "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz" 454 | integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== 455 | dependencies: 456 | array-buffer-byte-length "^1.0.0" 457 | call-bind "^1.0.5" 458 | es-get-iterator "^1.1.3" 459 | get-intrinsic "^1.2.2" 460 | is-arguments "^1.1.1" 461 | is-array-buffer "^3.0.2" 462 | is-date-object "^1.0.5" 463 | is-regex "^1.1.4" 464 | is-shared-array-buffer "^1.0.2" 465 | isarray "^2.0.5" 466 | object-is "^1.1.5" 467 | object-keys "^1.1.1" 468 | object.assign "^4.1.4" 469 | regexp.prototype.flags "^1.5.1" 470 | side-channel "^1.0.4" 471 | which-boxed-primitive "^1.0.2" 472 | which-collection "^1.0.1" 473 | which-typed-array "^1.1.13" 474 | 475 | deep-is@^0.1.3: 476 | version "0.1.4" 477 | resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" 478 | integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== 479 | 480 | default-user-agent@^1.0.0: 481 | version "1.0.0" 482 | resolved "https://registry.npmjs.org/default-user-agent/-/default-user-agent-1.0.0.tgz" 483 | integrity sha512-bDF7bg6OSNcSwFWPu4zYKpVkJZQYVrAANMYB8bc9Szem1D0yKdm4sa/rOCs2aC9+2GMqQ7KnwtZRvDhmLF0dXw== 484 | dependencies: 485 | os-name "~1.0.3" 486 | 487 | define-data-property@^1.0.1, define-data-property@^1.1.1: 488 | version "1.1.1" 489 | resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz" 490 | integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== 491 | dependencies: 492 | get-intrinsic "^1.2.1" 493 | gopd "^1.0.1" 494 | has-property-descriptors "^1.0.0" 495 | 496 | define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: 497 | version "1.2.1" 498 | resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" 499 | integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== 500 | dependencies: 501 | define-data-property "^1.0.1" 502 | has-property-descriptors "^1.0.0" 503 | object-keys "^1.1.1" 504 | 505 | delayed-stream@~1.0.0: 506 | version "1.0.0" 507 | resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" 508 | integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== 509 | 510 | destroy@^1.0.4: 511 | version "1.2.0" 512 | resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" 513 | integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== 514 | 515 | digest-header@^1.0.0: 516 | version "1.1.0" 517 | resolved "https://registry.npmjs.org/digest-header/-/digest-header-1.1.0.tgz" 518 | integrity sha512-glXVh42vz40yZb9Cq2oMOt70FIoWiv+vxNvdKdU8CwjLad25qHM3trLxhl9bVjdr6WaslIXhWpn0NO8T/67Qjg== 519 | 520 | dir-glob@^3.0.1: 521 | version "3.0.1" 522 | resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" 523 | integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== 524 | dependencies: 525 | path-type "^4.0.0" 526 | 527 | dns-packet@^5.2.2: 528 | version "5.6.1" 529 | resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz" 530 | integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== 531 | dependencies: 532 | "@leichtgewicht/ip-codec" "^2.0.1" 533 | 534 | doctrine@^3.0.0: 535 | version "3.0.0" 536 | resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" 537 | integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== 538 | dependencies: 539 | esutils "^2.0.2" 540 | 541 | duplexer@^0.1.1, duplexer@~0.1.1: 542 | version "0.1.2" 543 | resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" 544 | integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== 545 | 546 | end-of-stream@^1.1.0: 547 | version "1.4.4" 548 | resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" 549 | integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== 550 | dependencies: 551 | once "^1.4.0" 552 | 553 | es-errors@^1.0.0: 554 | version "1.0.0" 555 | resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.0.0.tgz" 556 | integrity sha512-yHV74THqMJUyFKkHyN7hyENcEZM3Dj2a2IrdClY+IT4BFQHkIVwlh8s6uZfjsFydMdNHv0F5mWgAA3ajFbsvVQ== 557 | 558 | es-get-iterator@^1.1.3: 559 | version "1.1.3" 560 | resolved "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz" 561 | integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== 562 | dependencies: 563 | call-bind "^1.0.2" 564 | get-intrinsic "^1.1.3" 565 | has-symbols "^1.0.3" 566 | is-arguments "^1.1.1" 567 | is-map "^2.0.2" 568 | is-set "^2.0.2" 569 | is-string "^1.0.7" 570 | isarray "^2.0.5" 571 | stop-iteration-iterator "^1.0.0" 572 | 573 | escape-string-regexp@^4.0.0: 574 | version "4.0.0" 575 | resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" 576 | integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== 577 | 578 | eslint-config-standard@^17.0.0: 579 | version "17.1.0" 580 | resolved "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz" 581 | integrity sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q== 582 | 583 | eslint-scope@^5.1.1: 584 | version "5.1.1" 585 | resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" 586 | integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== 587 | dependencies: 588 | esrecurse "^4.3.0" 589 | estraverse "^4.1.1" 590 | 591 | eslint-scope@^7.2.2: 592 | version "7.2.2" 593 | resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz" 594 | integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== 595 | dependencies: 596 | esrecurse "^4.3.0" 597 | estraverse "^5.2.0" 598 | 599 | eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: 600 | version "3.4.3" 601 | resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" 602 | integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== 603 | 604 | eslint@^8.17.0: 605 | version "8.56.0" 606 | resolved "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz" 607 | integrity sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ== 608 | dependencies: 609 | "@eslint-community/eslint-utils" "^4.2.0" 610 | "@eslint-community/regexpp" "^4.6.1" 611 | "@eslint/eslintrc" "^2.1.4" 612 | "@eslint/js" "8.56.0" 613 | "@humanwhocodes/config-array" "^0.11.13" 614 | "@humanwhocodes/module-importer" "^1.0.1" 615 | "@nodelib/fs.walk" "^1.2.8" 616 | "@ungap/structured-clone" "^1.2.0" 617 | ajv "^6.12.4" 618 | chalk "^4.0.0" 619 | cross-spawn "^7.0.2" 620 | debug "^4.3.2" 621 | doctrine "^3.0.0" 622 | escape-string-regexp "^4.0.0" 623 | eslint-scope "^7.2.2" 624 | eslint-visitor-keys "^3.4.3" 625 | espree "^9.6.1" 626 | esquery "^1.4.2" 627 | esutils "^2.0.2" 628 | fast-deep-equal "^3.1.3" 629 | file-entry-cache "^6.0.1" 630 | find-up "^5.0.0" 631 | glob-parent "^6.0.2" 632 | globals "^13.19.0" 633 | graphemer "^1.4.0" 634 | ignore "^5.2.0" 635 | imurmurhash "^0.1.4" 636 | is-glob "^4.0.0" 637 | is-path-inside "^3.0.3" 638 | js-yaml "^4.1.0" 639 | json-stable-stringify-without-jsonify "^1.0.1" 640 | levn "^0.4.1" 641 | lodash.merge "^4.6.2" 642 | minimatch "^3.1.2" 643 | natural-compare "^1.4.0" 644 | optionator "^0.9.3" 645 | strip-ansi "^6.0.1" 646 | text-table "^0.2.0" 647 | 648 | espree@^9.6.0, espree@^9.6.1: 649 | version "9.6.1" 650 | resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz" 651 | integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== 652 | dependencies: 653 | acorn "^8.9.0" 654 | acorn-jsx "^5.3.2" 655 | eslint-visitor-keys "^3.4.1" 656 | 657 | esquery@^1.4.2: 658 | version "1.5.0" 659 | resolved "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz" 660 | integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== 661 | dependencies: 662 | estraverse "^5.1.0" 663 | 664 | esrecurse@^4.3.0: 665 | version "4.3.0" 666 | resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" 667 | integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== 668 | dependencies: 669 | estraverse "^5.2.0" 670 | 671 | estraverse@^4.1.1: 672 | version "4.3.0" 673 | resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" 674 | integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== 675 | 676 | estraverse@^5.1.0, estraverse@^5.2.0: 677 | version "5.3.0" 678 | resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" 679 | integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== 680 | 681 | esutils@^2.0.2: 682 | version "2.0.3" 683 | resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" 684 | integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== 685 | 686 | event-stream@^4.0.0: 687 | version "4.0.1" 688 | resolved "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz" 689 | integrity sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA== 690 | dependencies: 691 | duplexer "^0.1.1" 692 | from "^0.1.7" 693 | map-stream "0.0.7" 694 | pause-stream "^0.0.11" 695 | split "^1.0.1" 696 | stream-combiner "^0.2.2" 697 | through "^2.3.8" 698 | 699 | fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: 700 | version "3.1.3" 701 | resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" 702 | integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== 703 | 704 | fast-glob@^3.2.9: 705 | version "3.3.2" 706 | resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz" 707 | integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== 708 | dependencies: 709 | "@nodelib/fs.stat" "^2.0.2" 710 | "@nodelib/fs.walk" "^1.2.3" 711 | glob-parent "^5.1.2" 712 | merge2 "^1.3.0" 713 | micromatch "^4.0.4" 714 | 715 | fast-json-stable-stringify@^2.0.0: 716 | version "2.1.0" 717 | resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" 718 | integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== 719 | 720 | fast-levenshtein@^2.0.6: 721 | version "2.0.6" 722 | resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" 723 | integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== 724 | 725 | fast-srp-hap@~2.0.4: 726 | version "2.0.4" 727 | resolved "https://registry.npmjs.org/fast-srp-hap/-/fast-srp-hap-2.0.4.tgz" 728 | integrity sha512-lHRYYaaIbMrhZtsdGTwPN82UbqD9Bv8QfOlKs+Dz6YRnByZifOh93EYmf2iEWFtkOEIqR2IK8cFD0UN5wLIWBQ== 729 | 730 | fastq@^1.6.0: 731 | version "1.17.0" 732 | resolved "https://registry.npmjs.org/fastq/-/fastq-1.17.0.tgz" 733 | integrity sha512-zGygtijUMT7jnk3h26kUms3BkSDp4IfIKjmnqI2tvx6nuBfiF1UqOxbnLfzdv+apBy+53oaImsKtMw/xYbW+1w== 734 | dependencies: 735 | reusify "^1.0.4" 736 | 737 | file-entry-cache@^6.0.1: 738 | version "6.0.1" 739 | resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" 740 | integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== 741 | dependencies: 742 | flat-cache "^3.0.4" 743 | 744 | fill-range@^7.1.1: 745 | version "7.1.1" 746 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" 747 | integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== 748 | dependencies: 749 | to-regex-range "^5.0.1" 750 | 751 | find-up@^5.0.0: 752 | version "5.0.0" 753 | resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" 754 | integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== 755 | dependencies: 756 | locate-path "^6.0.0" 757 | path-exists "^4.0.0" 758 | 759 | flat-cache@^3.0.4: 760 | version "3.2.0" 761 | resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz" 762 | integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== 763 | dependencies: 764 | flatted "^3.2.9" 765 | keyv "^4.5.3" 766 | rimraf "^3.0.2" 767 | 768 | flatted@^3.2.9: 769 | version "3.2.9" 770 | resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz" 771 | integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== 772 | 773 | follow-redirects@^1.15.0, follow-redirects@^1.15.6: 774 | version "1.15.6" 775 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" 776 | integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== 777 | 778 | for-each@^0.3.3: 779 | version "0.3.3" 780 | resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" 781 | integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== 782 | dependencies: 783 | is-callable "^1.1.3" 784 | 785 | form-data-encoder@^1.7.2: 786 | version "1.9.0" 787 | resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.9.0.tgz" 788 | integrity sha512-rahaRMkN8P8d/tgK/BLPX+WBVM27NbvdXBxqQujBtkDAIFspaRqN7Od7lfdGQA6KAD+f82fYCLBq1ipvcu8qLw== 789 | 790 | form-data@^4.0.0: 791 | version "4.0.0" 792 | resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" 793 | integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== 794 | dependencies: 795 | asynckit "^0.4.0" 796 | combined-stream "^1.0.8" 797 | mime-types "^2.1.12" 798 | 799 | formdata-node@^4.3.3: 800 | version "4.4.1" 801 | resolved "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz" 802 | integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== 803 | dependencies: 804 | node-domexception "1.0.0" 805 | web-streams-polyfill "4.0.0-beta.3" 806 | 807 | formstream@^1.1.1: 808 | version "1.3.1" 809 | resolved "https://registry.npmjs.org/formstream/-/formstream-1.3.1.tgz" 810 | integrity sha512-FkW++ub+VbE5dpwukJVDizNWhSgp8FhmhI65pF7BZSVStBqe6Wgxe2Z9/Vhsn7l7nXCPwP+G1cyYlX8VwWOf0g== 811 | dependencies: 812 | destroy "^1.0.4" 813 | mime "^2.5.2" 814 | pause-stream "~0.0.11" 815 | 816 | from@^0.1.7: 817 | version "0.1.7" 818 | resolved "https://registry.npmjs.org/from/-/from-0.1.7.tgz" 819 | integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== 820 | 821 | fs-extra@^10.0.0, fs-extra@^10.1.0: 822 | version "10.1.0" 823 | resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz" 824 | integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== 825 | dependencies: 826 | graceful-fs "^4.2.0" 827 | jsonfile "^6.0.1" 828 | universalify "^2.0.0" 829 | 830 | fs.realpath@^1.0.0: 831 | version "1.0.0" 832 | resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" 833 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== 834 | 835 | function-bind@^1.1.2: 836 | version "1.1.2" 837 | resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" 838 | integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== 839 | 840 | functions-have-names@^1.2.3: 841 | version "1.2.3" 842 | resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" 843 | integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== 844 | 845 | futoin-hkdf@~1.4.3: 846 | version "1.4.3" 847 | resolved "https://registry.npmjs.org/futoin-hkdf/-/futoin-hkdf-1.4.3.tgz" 848 | integrity sha512-K4MIe2xSVRMYxsA4w0ap5fp1C2hA9StA2Ad1JZHX57VMCdHIRB5BSrd1FhuadTQG9MkjggaTCrw7v5XXFyY3/w== 849 | 850 | get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: 851 | version "1.2.3" 852 | resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.3.tgz" 853 | integrity sha512-JIcZczvcMVE7AUOP+X72bh8HqHBRxFdz5PDHYtNG/lE3yk9b3KZBJlwFcTyPYjg3L4RLLmZJzvjxhaZVapxFrQ== 854 | dependencies: 855 | es-errors "^1.0.0" 856 | function-bind "^1.1.2" 857 | has-proto "^1.0.1" 858 | has-symbols "^1.0.3" 859 | hasown "^2.0.0" 860 | 861 | glob-parent@^5.1.2: 862 | version "5.1.2" 863 | resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" 864 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 865 | dependencies: 866 | is-glob "^4.0.1" 867 | 868 | glob-parent@^6.0.2: 869 | version "6.0.2" 870 | resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" 871 | integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== 872 | dependencies: 873 | is-glob "^4.0.3" 874 | 875 | glob@^7.1.3: 876 | version "7.2.3" 877 | resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" 878 | integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== 879 | dependencies: 880 | fs.realpath "^1.0.0" 881 | inflight "^1.0.4" 882 | inherits "2" 883 | minimatch "^3.1.1" 884 | once "^1.3.0" 885 | path-is-absolute "^1.0.0" 886 | 887 | globals@^13.19.0: 888 | version "13.24.0" 889 | resolved "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz" 890 | integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== 891 | dependencies: 892 | type-fest "^0.20.2" 893 | 894 | globby@^11.1.0: 895 | version "11.1.0" 896 | resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" 897 | integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== 898 | dependencies: 899 | array-union "^2.1.0" 900 | dir-glob "^3.0.1" 901 | fast-glob "^3.2.9" 902 | ignore "^5.2.0" 903 | merge2 "^1.4.1" 904 | slash "^3.0.0" 905 | 906 | gopd@^1.0.1: 907 | version "1.0.1" 908 | resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" 909 | integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== 910 | dependencies: 911 | get-intrinsic "^1.1.3" 912 | 913 | graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.2.0: 914 | version "4.2.11" 915 | resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" 916 | integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== 917 | 918 | graphemer@^1.4.0: 919 | version "1.4.0" 920 | resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" 921 | integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== 922 | 923 | hap-nodejs@~0.11.1: 924 | version "0.11.1" 925 | resolved "https://registry.npmjs.org/hap-nodejs/-/hap-nodejs-0.11.1.tgz" 926 | integrity sha512-hJuGyjng2jlzhZsviWCldaokT7l7BE3iGmWdlE6DNmQFDTmiBN3deNksAZ2nt7qp5jYEv7ZUvW7WBZqJsLh3ww== 927 | dependencies: 928 | "@homebridge/ciao" "^1.1.5" 929 | "@homebridge/dbus-native" "^0.5.1" 930 | bonjour-hap "~3.6.4" 931 | debug "^4.3.4" 932 | fast-srp-hap "~2.0.4" 933 | futoin-hkdf "~1.4.3" 934 | node-persist "^0.0.11" 935 | source-map-support "^0.5.21" 936 | tslib "^2.4.0" 937 | tweetnacl "^1.0.3" 938 | 939 | has-bigints@^1.0.1: 940 | version "1.0.2" 941 | resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" 942 | integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== 943 | 944 | has-flag@^4.0.0: 945 | version "4.0.0" 946 | resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" 947 | integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== 948 | 949 | has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: 950 | version "1.0.1" 951 | resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz" 952 | integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== 953 | dependencies: 954 | get-intrinsic "^1.2.2" 955 | 956 | has-proto@^1.0.1: 957 | version "1.0.1" 958 | resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz" 959 | integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== 960 | 961 | has-symbols@^1.0.2, has-symbols@^1.0.3: 962 | version "1.0.3" 963 | resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" 964 | integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== 965 | 966 | has-tostringtag@^1.0.0, has-tostringtag@^1.0.1: 967 | version "1.0.2" 968 | resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" 969 | integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== 970 | dependencies: 971 | has-symbols "^1.0.3" 972 | 973 | hasown@^2.0.0: 974 | version "2.0.0" 975 | resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz" 976 | integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== 977 | dependencies: 978 | function-bind "^1.1.2" 979 | 980 | hexy@^0.2.10: 981 | version "0.2.11" 982 | resolved "https://registry.npmjs.org/hexy/-/hexy-0.2.11.tgz" 983 | integrity sha512-ciq6hFsSG/Bpt2DmrZJtv+56zpPdnq+NQ4ijEFrveKN0ZG1mhl/LdT1NQZ9se6ty1fACcI4d4vYqC9v8EYpH2A== 984 | 985 | homebridge@1.6.1: 986 | version "1.6.1" 987 | resolved "https://registry.npmjs.org/homebridge/-/homebridge-1.6.1.tgz" 988 | integrity sha512-hDhSaBDHFbB8wQQuZKbistYj1gjTIcNWmusqgEUb0Umk76Hs+G6VKRTkOEEVuxRaQWoK5hRM5rJTsCGAMCj5cA== 989 | dependencies: 990 | chalk "^4.1.2" 991 | commander "5.1.0" 992 | fs-extra "^10.1.0" 993 | hap-nodejs "~0.11.1" 994 | qrcode-terminal "^0.12.0" 995 | semver "^7.3.7" 996 | source-map-support "^0.5.21" 997 | 998 | ignore@^5.2.0: 999 | version "5.3.1" 1000 | resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz" 1001 | integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== 1002 | 1003 | import-fresh@^3.2.1: 1004 | version "3.3.0" 1005 | resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" 1006 | integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== 1007 | dependencies: 1008 | parent-module "^1.0.0" 1009 | resolve-from "^4.0.0" 1010 | 1011 | imurmurhash@^0.1.4: 1012 | version "0.1.4" 1013 | resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" 1014 | integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== 1015 | 1016 | inflight@^1.0.4: 1017 | version "1.0.6" 1018 | resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" 1019 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== 1020 | dependencies: 1021 | once "^1.3.0" 1022 | wrappy "1" 1023 | 1024 | inherits@2, inherits@2.0.4: 1025 | version "2.0.4" 1026 | resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" 1027 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 1028 | 1029 | internal-slot@^1.0.4: 1030 | version "1.0.6" 1031 | resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz" 1032 | integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== 1033 | dependencies: 1034 | get-intrinsic "^1.2.2" 1035 | hasown "^2.0.0" 1036 | side-channel "^1.0.4" 1037 | 1038 | ip@^1.1.8: 1039 | version "1.1.8" 1040 | resolved "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz" 1041 | integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== 1042 | 1043 | is-arguments@^1.1.1: 1044 | version "1.1.1" 1045 | resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz" 1046 | integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== 1047 | dependencies: 1048 | call-bind "^1.0.2" 1049 | has-tostringtag "^1.0.0" 1050 | 1051 | is-array-buffer@^3.0.2, is-array-buffer@^3.0.4: 1052 | version "3.0.4" 1053 | resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz" 1054 | integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== 1055 | dependencies: 1056 | call-bind "^1.0.2" 1057 | get-intrinsic "^1.2.1" 1058 | 1059 | is-bigint@^1.0.1: 1060 | version "1.0.4" 1061 | resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" 1062 | integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== 1063 | dependencies: 1064 | has-bigints "^1.0.1" 1065 | 1066 | is-boolean-object@^1.1.0: 1067 | version "1.1.2" 1068 | resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" 1069 | integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== 1070 | dependencies: 1071 | call-bind "^1.0.2" 1072 | has-tostringtag "^1.0.0" 1073 | 1074 | is-buffer@~1.1.6: 1075 | version "1.1.6" 1076 | resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" 1077 | integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== 1078 | 1079 | is-callable@^1.1.3: 1080 | version "1.2.7" 1081 | resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" 1082 | integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== 1083 | 1084 | is-date-object@^1.0.5: 1085 | version "1.0.5" 1086 | resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" 1087 | integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== 1088 | dependencies: 1089 | has-tostringtag "^1.0.0" 1090 | 1091 | is-extglob@^2.1.1: 1092 | version "2.1.1" 1093 | resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" 1094 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== 1095 | 1096 | is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: 1097 | version "4.0.3" 1098 | resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" 1099 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 1100 | dependencies: 1101 | is-extglob "^2.1.1" 1102 | 1103 | is-map@^2.0.1, is-map@^2.0.2: 1104 | version "2.0.2" 1105 | resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz" 1106 | integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== 1107 | 1108 | is-number-object@^1.0.4: 1109 | version "1.0.7" 1110 | resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" 1111 | integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== 1112 | dependencies: 1113 | has-tostringtag "^1.0.0" 1114 | 1115 | is-number@^7.0.0: 1116 | version "7.0.0" 1117 | resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" 1118 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 1119 | 1120 | is-path-inside@^3.0.3: 1121 | version "3.0.3" 1122 | resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" 1123 | integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== 1124 | 1125 | is-promise@^2.1.0: 1126 | version "2.2.2" 1127 | resolved "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz" 1128 | integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== 1129 | 1130 | is-regex@^1.1.4: 1131 | version "1.1.4" 1132 | resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" 1133 | integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== 1134 | dependencies: 1135 | call-bind "^1.0.2" 1136 | has-tostringtag "^1.0.0" 1137 | 1138 | is-set@^2.0.1, is-set@^2.0.2: 1139 | version "2.0.2" 1140 | resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz" 1141 | integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== 1142 | 1143 | is-shared-array-buffer@^1.0.2: 1144 | version "1.0.2" 1145 | resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz" 1146 | integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== 1147 | dependencies: 1148 | call-bind "^1.0.2" 1149 | 1150 | is-string@^1.0.5, is-string@^1.0.7: 1151 | version "1.0.7" 1152 | resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" 1153 | integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== 1154 | dependencies: 1155 | has-tostringtag "^1.0.0" 1156 | 1157 | is-symbol@^1.0.3: 1158 | version "1.0.4" 1159 | resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" 1160 | integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== 1161 | dependencies: 1162 | has-symbols "^1.0.2" 1163 | 1164 | is-weakmap@^2.0.1: 1165 | version "2.0.1" 1166 | resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz" 1167 | integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== 1168 | 1169 | is-weakset@^2.0.1: 1170 | version "2.0.2" 1171 | resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz" 1172 | integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== 1173 | dependencies: 1174 | call-bind "^1.0.2" 1175 | get-intrinsic "^1.1.1" 1176 | 1177 | isarray@^2.0.5: 1178 | version "2.0.5" 1179 | resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" 1180 | integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== 1181 | 1182 | isexe@^2.0.0: 1183 | version "2.0.0" 1184 | resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" 1185 | integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== 1186 | 1187 | js-md5@^0.7.3: 1188 | version "0.7.3" 1189 | resolved "https://registry.npmjs.org/js-md5/-/js-md5-0.7.3.tgz" 1190 | integrity sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ== 1191 | 1192 | js-sha1@^0.6.0: 1193 | version "0.6.0" 1194 | resolved "https://registry.npmjs.org/js-sha1/-/js-sha1-0.6.0.tgz" 1195 | integrity sha512-01gwBFreYydzmU9BmZxpVk6svJJHrVxEN3IOiGl6VO93bVKYETJ0sIth6DASI6mIFdt7NmfX9UiByRzsYHGU9w== 1196 | 1197 | js-yaml@^4.1.0: 1198 | version "4.1.0" 1199 | resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" 1200 | integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== 1201 | dependencies: 1202 | argparse "^2.0.1" 1203 | 1204 | json-buffer@3.0.1: 1205 | version "3.0.1" 1206 | resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" 1207 | integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== 1208 | 1209 | json-schema-traverse@^0.4.1: 1210 | version "0.4.1" 1211 | resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" 1212 | integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== 1213 | 1214 | json-stable-stringify-without-jsonify@^1.0.1: 1215 | version "1.0.1" 1216 | resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" 1217 | integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== 1218 | 1219 | jsonfile@^6.0.1: 1220 | version "6.1.0" 1221 | resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" 1222 | integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== 1223 | dependencies: 1224 | universalify "^2.0.0" 1225 | optionalDependencies: 1226 | graceful-fs "^4.1.6" 1227 | 1228 | keyv@^4.5.3: 1229 | version "4.5.4" 1230 | resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" 1231 | integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== 1232 | dependencies: 1233 | json-buffer "3.0.1" 1234 | 1235 | levn@^0.4.1: 1236 | version "0.4.1" 1237 | resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" 1238 | integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== 1239 | dependencies: 1240 | prelude-ls "^1.2.1" 1241 | type-check "~0.4.0" 1242 | 1243 | locate-path@^6.0.0: 1244 | version "6.0.0" 1245 | resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" 1246 | integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== 1247 | dependencies: 1248 | p-locate "^5.0.0" 1249 | 1250 | lodash.merge@^4.6.2: 1251 | version "4.6.2" 1252 | resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" 1253 | integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== 1254 | 1255 | lodash@4: 1256 | version "4.17.21" 1257 | resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" 1258 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 1259 | 1260 | lowdb@^1.0.0: 1261 | version "1.0.0" 1262 | resolved "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz" 1263 | integrity sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ== 1264 | dependencies: 1265 | graceful-fs "^4.1.3" 1266 | is-promise "^2.1.0" 1267 | lodash "4" 1268 | pify "^3.0.0" 1269 | steno "^0.4.1" 1270 | 1271 | lru-cache@^6.0.0: 1272 | version "6.0.0" 1273 | resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" 1274 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 1275 | dependencies: 1276 | yallist "^4.0.0" 1277 | 1278 | map-stream@0.0.7: 1279 | version "0.0.7" 1280 | resolved "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz" 1281 | integrity sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ== 1282 | 1283 | md5@^2.2.1: 1284 | version "2.3.0" 1285 | resolved "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz" 1286 | integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== 1287 | dependencies: 1288 | charenc "0.0.2" 1289 | crypt "0.0.2" 1290 | is-buffer "~1.1.6" 1291 | 1292 | merge2@^1.3.0, merge2@^1.4.1: 1293 | version "1.4.1" 1294 | resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" 1295 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== 1296 | 1297 | micromatch@^4.0.4: 1298 | version "4.0.5" 1299 | resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" 1300 | integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== 1301 | dependencies: 1302 | braces "^3.0.2" 1303 | picomatch "^2.3.1" 1304 | 1305 | mime-db@1.52.0: 1306 | version "1.52.0" 1307 | resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" 1308 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== 1309 | 1310 | mime-types@^2.1.12, mime-types@^2.1.35: 1311 | version "2.1.35" 1312 | resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" 1313 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== 1314 | dependencies: 1315 | mime-db "1.52.0" 1316 | 1317 | mime@^2.5.2: 1318 | version "2.6.0" 1319 | resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz" 1320 | integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== 1321 | 1322 | minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: 1323 | version "3.1.2" 1324 | resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" 1325 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 1326 | dependencies: 1327 | brace-expansion "^1.1.7" 1328 | 1329 | minimist@^1.1.0, minimist@^1.2.6: 1330 | version "1.2.8" 1331 | resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" 1332 | integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== 1333 | 1334 | mkdirp@~0.5.1: 1335 | version "0.5.6" 1336 | resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" 1337 | integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== 1338 | dependencies: 1339 | minimist "^1.2.6" 1340 | 1341 | moment@2.29.4: 1342 | version "2.29.4" 1343 | resolved "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz" 1344 | integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== 1345 | 1346 | ms@2.1.2: 1347 | version "2.1.2" 1348 | resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" 1349 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 1350 | 1351 | multicast-dns-service-types@^1.1.0: 1352 | version "1.1.0" 1353 | resolved "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz" 1354 | integrity sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ== 1355 | 1356 | multicast-dns@^7.2.5: 1357 | version "7.2.5" 1358 | resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz" 1359 | integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== 1360 | dependencies: 1361 | dns-packet "^5.2.2" 1362 | thunky "^1.0.2" 1363 | 1364 | natural-compare-lite@^1.4.0: 1365 | version "1.4.0" 1366 | resolved "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz" 1367 | integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== 1368 | 1369 | natural-compare@^1.4.0: 1370 | version "1.4.0" 1371 | resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" 1372 | integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== 1373 | 1374 | node-domexception@1.0.0: 1375 | version "1.0.0" 1376 | resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz" 1377 | integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== 1378 | 1379 | node-persist@^0.0.11: 1380 | version "0.0.11" 1381 | resolved "https://registry.npmjs.org/node-persist/-/node-persist-0.0.11.tgz" 1382 | integrity sha512-J3EPzQDgPxPBID7TqHSd5KkpTULFqJUvYDoISfOWg9EihpeVCH3b6YQeDeubzVuc4e6+aiVmkz2sdkWI4K+ghA== 1383 | dependencies: 1384 | mkdirp "~0.5.1" 1385 | q "~1.1.1" 1386 | 1387 | object-inspect@^1.9.0: 1388 | version "1.13.1" 1389 | resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz" 1390 | integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== 1391 | 1392 | object-is@^1.1.5: 1393 | version "1.1.5" 1394 | resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz" 1395 | integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== 1396 | dependencies: 1397 | call-bind "^1.0.2" 1398 | define-properties "^1.1.3" 1399 | 1400 | object-keys@^1.1.1: 1401 | version "1.1.1" 1402 | resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" 1403 | integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== 1404 | 1405 | object.assign@^4.1.4: 1406 | version "4.1.5" 1407 | resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz" 1408 | integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== 1409 | dependencies: 1410 | call-bind "^1.0.5" 1411 | define-properties "^1.2.1" 1412 | has-symbols "^1.0.3" 1413 | object-keys "^1.1.1" 1414 | 1415 | once@^1.3.0, once@^1.3.1, once@^1.4.0: 1416 | version "1.4.0" 1417 | resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" 1418 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 1419 | dependencies: 1420 | wrappy "1" 1421 | 1422 | optionator@^0.9.3: 1423 | version "0.9.3" 1424 | resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz" 1425 | integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== 1426 | dependencies: 1427 | "@aashutoshrathi/word-wrap" "^1.2.3" 1428 | deep-is "^0.1.3" 1429 | fast-levenshtein "^2.0.6" 1430 | levn "^0.4.1" 1431 | prelude-ls "^1.2.1" 1432 | type-check "^0.4.0" 1433 | 1434 | os-name@~1.0.3: 1435 | version "1.0.3" 1436 | resolved "https://registry.npmjs.org/os-name/-/os-name-1.0.3.tgz" 1437 | integrity sha512-f5estLO2KN8vgtTRaILIgEGBoBrMnZ3JQ7W9TMZCnOIGwHe8TRGSpcagnWDo+Dfhd/z08k9Xe75hvciJJ8Qaew== 1438 | dependencies: 1439 | osx-release "^1.0.0" 1440 | win-release "^1.0.0" 1441 | 1442 | osx-release@^1.0.0: 1443 | version "1.1.0" 1444 | resolved "https://registry.npmjs.org/osx-release/-/osx-release-1.1.0.tgz" 1445 | integrity sha512-ixCMMwnVxyHFQLQnINhmIpWqXIfS2YOXchwQrk+OFzmo6nDjQ0E4KXAyyUh0T0MZgV4bUhkRrAbVqlE4yLVq4A== 1446 | dependencies: 1447 | minimist "^1.1.0" 1448 | 1449 | p-limit@^3.0.2: 1450 | version "3.1.0" 1451 | resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" 1452 | integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== 1453 | dependencies: 1454 | yocto-queue "^0.1.0" 1455 | 1456 | p-locate@^5.0.0: 1457 | version "5.0.0" 1458 | resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" 1459 | integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== 1460 | dependencies: 1461 | p-limit "^3.0.2" 1462 | 1463 | parent-module@^1.0.0: 1464 | version "1.0.1" 1465 | resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" 1466 | integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== 1467 | dependencies: 1468 | callsites "^3.0.0" 1469 | 1470 | path-exists@^4.0.0: 1471 | version "4.0.0" 1472 | resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" 1473 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== 1474 | 1475 | path-is-absolute@^1.0.0: 1476 | version "1.0.1" 1477 | resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" 1478 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== 1479 | 1480 | path-key@^3.1.0: 1481 | version "3.1.1" 1482 | resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" 1483 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 1484 | 1485 | path-type@^4.0.0: 1486 | version "4.0.0" 1487 | resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" 1488 | integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== 1489 | 1490 | pause-stream@^0.0.11, pause-stream@~0.0.11: 1491 | version "0.0.11" 1492 | resolved "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz" 1493 | integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A== 1494 | dependencies: 1495 | through "~2.3" 1496 | 1497 | picomatch@^2.3.1: 1498 | version "2.3.1" 1499 | resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" 1500 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 1501 | 1502 | pify@^3.0.0: 1503 | version "3.0.0" 1504 | resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" 1505 | integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== 1506 | 1507 | prelude-ls@^1.2.1: 1508 | version "1.2.1" 1509 | resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" 1510 | integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== 1511 | 1512 | proxy-from-env@^1.1.0: 1513 | version "1.1.0" 1514 | resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" 1515 | integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== 1516 | 1517 | pump@^3.0.0: 1518 | version "3.0.0" 1519 | resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" 1520 | integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== 1521 | dependencies: 1522 | end-of-stream "^1.1.0" 1523 | once "^1.3.1" 1524 | 1525 | punycode@^2.1.0: 1526 | version "2.3.1" 1527 | resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" 1528 | integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== 1529 | 1530 | q@~1.1.1: 1531 | version "1.1.2" 1532 | resolved "https://registry.npmjs.org/q/-/q-1.1.2.tgz" 1533 | integrity sha512-ROtylwux7Vkc4C07oKE/ReigUmb33kVoLtcR4SJ1QVqwaZkBEDL3vX4/kwFzIERQ5PfCl0XafbU8u2YUhyGgVA== 1534 | 1535 | qrcode-terminal@^0.12.0: 1536 | version "0.12.0" 1537 | resolved "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz" 1538 | integrity sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ== 1539 | 1540 | qs@^6.11.2: 1541 | version "6.11.2" 1542 | resolved "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz" 1543 | integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== 1544 | dependencies: 1545 | side-channel "^1.0.4" 1546 | 1547 | querystring@0.2.1: 1548 | version "0.2.1" 1549 | resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz" 1550 | integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== 1551 | 1552 | queue-microtask@^1.2.2: 1553 | version "1.2.3" 1554 | resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" 1555 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== 1556 | 1557 | regexp.prototype.flags@^1.5.1: 1558 | version "1.5.1" 1559 | resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz" 1560 | integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== 1561 | dependencies: 1562 | call-bind "^1.0.2" 1563 | define-properties "^1.2.0" 1564 | set-function-name "^2.0.0" 1565 | 1566 | resolve-from@^4.0.0: 1567 | version "4.0.0" 1568 | resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" 1569 | integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== 1570 | 1571 | reusify@^1.0.4: 1572 | version "1.0.4" 1573 | resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" 1574 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== 1575 | 1576 | rimraf@^3.0.2: 1577 | version "3.0.2" 1578 | resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" 1579 | integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== 1580 | dependencies: 1581 | glob "^7.1.3" 1582 | 1583 | rotating-file-stream@^2.1.5: 1584 | version "2.1.6" 1585 | resolved "https://registry.npmjs.org/rotating-file-stream/-/rotating-file-stream-2.1.6.tgz" 1586 | integrity sha512-qS0ndAlDu80MMXeRonqGMXslF0FErzcUSbcXhus3asRG4cvCS79hc5f7s0x4bPAsH6wAwyHVIeARg69VUe3JmQ== 1587 | 1588 | run-parallel@^1.1.9: 1589 | version "1.2.0" 1590 | resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" 1591 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== 1592 | dependencies: 1593 | queue-microtask "^1.2.2" 1594 | 1595 | safe-buffer@^5.1.1: 1596 | version "5.2.1" 1597 | resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" 1598 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 1599 | 1600 | sax@>=0.6.0: 1601 | version "1.3.0" 1602 | resolved "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz" 1603 | integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== 1604 | 1605 | semver@^5.0.1: 1606 | version "5.7.2" 1607 | resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" 1608 | integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== 1609 | 1610 | semver@^7.3.7: 1611 | version "7.5.4" 1612 | resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz" 1613 | integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== 1614 | dependencies: 1615 | lru-cache "^6.0.0" 1616 | 1617 | set-function-length@^1.1.1: 1618 | version "1.2.0" 1619 | resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz" 1620 | integrity sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w== 1621 | dependencies: 1622 | define-data-property "^1.1.1" 1623 | function-bind "^1.1.2" 1624 | get-intrinsic "^1.2.2" 1625 | gopd "^1.0.1" 1626 | has-property-descriptors "^1.0.1" 1627 | 1628 | set-function-name@^2.0.0: 1629 | version "2.0.1" 1630 | resolved "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz" 1631 | integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== 1632 | dependencies: 1633 | define-data-property "^1.0.1" 1634 | functions-have-names "^1.2.3" 1635 | has-property-descriptors "^1.0.0" 1636 | 1637 | shebang-command@^2.0.0: 1638 | version "2.0.0" 1639 | resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" 1640 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 1641 | dependencies: 1642 | shebang-regex "^3.0.0" 1643 | 1644 | shebang-regex@^3.0.0: 1645 | version "3.0.0" 1646 | resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" 1647 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 1648 | 1649 | side-channel@^1.0.4: 1650 | version "1.0.4" 1651 | resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" 1652 | integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== 1653 | dependencies: 1654 | call-bind "^1.0.0" 1655 | get-intrinsic "^1.0.2" 1656 | object-inspect "^1.9.0" 1657 | 1658 | slash@^3.0.0: 1659 | version "3.0.0" 1660 | resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" 1661 | integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== 1662 | 1663 | source-map-support@^0.5.21: 1664 | version "0.5.21" 1665 | resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" 1666 | integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== 1667 | dependencies: 1668 | buffer-from "^1.0.0" 1669 | source-map "^0.6.0" 1670 | 1671 | source-map@^0.6.0: 1672 | version "0.6.1" 1673 | resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" 1674 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 1675 | 1676 | split@^1.0.1: 1677 | version "1.0.1" 1678 | resolved "https://registry.npmjs.org/split/-/split-1.0.1.tgz" 1679 | integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== 1680 | dependencies: 1681 | through "2" 1682 | 1683 | steno@^0.4.1: 1684 | version "0.4.4" 1685 | resolved "https://registry.npmjs.org/steno/-/steno-0.4.4.tgz" 1686 | integrity sha512-EEHMVYHNXFHfGtgjNITnka0aHhiAlo93F7z2/Pwd+g0teG9CnM3JIINM7hVVB5/rhw9voufD7Wukwgtw2uqh6w== 1687 | dependencies: 1688 | graceful-fs "^4.1.3" 1689 | 1690 | stop-iteration-iterator@^1.0.0: 1691 | version "1.0.0" 1692 | resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz" 1693 | integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== 1694 | dependencies: 1695 | internal-slot "^1.0.4" 1696 | 1697 | stream-combiner@^0.2.2: 1698 | version "0.2.2" 1699 | resolved "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz" 1700 | integrity sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ== 1701 | dependencies: 1702 | duplexer "~0.1.1" 1703 | through "~2.3.4" 1704 | 1705 | strip-ansi@^6.0.0, strip-ansi@^6.0.1: 1706 | version "6.0.1" 1707 | resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" 1708 | integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== 1709 | dependencies: 1710 | ansi-regex "^5.0.1" 1711 | 1712 | strip-json-comments@^3.1.1: 1713 | version "3.1.1" 1714 | resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" 1715 | integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== 1716 | 1717 | supports-color@^7.1.0: 1718 | version "7.2.0" 1719 | resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" 1720 | integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== 1721 | dependencies: 1722 | has-flag "^4.0.0" 1723 | 1724 | text-table@^0.2.0: 1725 | version "0.2.0" 1726 | resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" 1727 | integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== 1728 | 1729 | through@2, through@^2.3.8, through@~2.3, through@~2.3.4: 1730 | version "2.3.8" 1731 | resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" 1732 | integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== 1733 | 1734 | thunky@^1.0.2: 1735 | version "1.1.0" 1736 | resolved "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz" 1737 | integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== 1738 | 1739 | to-regex-range@^5.0.1: 1740 | version "5.0.1" 1741 | resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" 1742 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 1743 | dependencies: 1744 | is-number "^7.0.0" 1745 | 1746 | tslib@^1.8.1: 1747 | version "1.14.1" 1748 | resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" 1749 | integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== 1750 | 1751 | tslib@^2.4.0, tslib@^2.6.2: 1752 | version "2.6.2" 1753 | resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz" 1754 | integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== 1755 | 1756 | tsutils@^3.21.0: 1757 | version "3.21.0" 1758 | resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" 1759 | integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== 1760 | dependencies: 1761 | tslib "^1.8.1" 1762 | 1763 | tweetnacl@^1.0.3: 1764 | version "1.0.3" 1765 | resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz" 1766 | integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== 1767 | 1768 | type-check@^0.4.0, type-check@~0.4.0: 1769 | version "0.4.0" 1770 | resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" 1771 | integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== 1772 | dependencies: 1773 | prelude-ls "^1.2.1" 1774 | 1775 | type-fest@^0.20.2: 1776 | version "0.20.2" 1777 | resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" 1778 | integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== 1779 | 1780 | undici@^5.22.1: 1781 | version "5.28.4" 1782 | resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" 1783 | integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== 1784 | dependencies: 1785 | "@fastify/busboy" "^2.0.0" 1786 | 1787 | universalify@^2.0.0: 1788 | version "2.0.1" 1789 | resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" 1790 | integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== 1791 | 1792 | uri-js@^4.2.2: 1793 | version "4.4.1" 1794 | resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" 1795 | integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== 1796 | dependencies: 1797 | punycode "^2.1.0" 1798 | 1799 | urllib@3.18.0: 1800 | version "3.18.0" 1801 | resolved "https://registry.npmjs.org/urllib/-/urllib-3.18.0.tgz" 1802 | integrity sha512-y59V+iidWiiE9Y5/y0hhNNIa0r3lTVsWQFlucD0yLP7MGgMmRUeL1/6vqOk+MtgtfTaul7pBpcn5aXedrEuhtw== 1803 | dependencies: 1804 | default-user-agent "^1.0.0" 1805 | digest-header "^1.0.0" 1806 | form-data-encoder "^1.7.2" 1807 | formdata-node "^4.3.3" 1808 | formstream "^1.1.1" 1809 | mime-types "^2.1.35" 1810 | pump "^3.0.0" 1811 | qs "^6.11.2" 1812 | undici "^5.22.1" 1813 | ylru "^1.3.2" 1814 | 1815 | utf8@3.0.0, utf8@^3.0.0: 1816 | version "3.0.0" 1817 | resolved "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz" 1818 | integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== 1819 | 1820 | uuid-by-string@4.0.0: 1821 | version "4.0.0" 1822 | resolved "https://registry.npmjs.org/uuid-by-string/-/uuid-by-string-4.0.0.tgz" 1823 | integrity sha512-88ZSfcSkN04juiLqSsuyteqlSrXNFdsEPzSv3urnElDXNsZUXQN0smeTnh99x2DE15SCUQNgqKBfro54CuzHNQ== 1824 | dependencies: 1825 | js-md5 "^0.7.3" 1826 | js-sha1 "^0.6.0" 1827 | 1828 | uuid@8.3.2: 1829 | version "8.3.2" 1830 | resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" 1831 | integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== 1832 | 1833 | web-streams-polyfill@4.0.0-beta.3: 1834 | version "4.0.0-beta.3" 1835 | resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz" 1836 | integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== 1837 | 1838 | which-boxed-primitive@^1.0.2: 1839 | version "1.0.2" 1840 | resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" 1841 | integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== 1842 | dependencies: 1843 | is-bigint "^1.0.1" 1844 | is-boolean-object "^1.1.0" 1845 | is-number-object "^1.0.4" 1846 | is-string "^1.0.5" 1847 | is-symbol "^1.0.3" 1848 | 1849 | which-collection@^1.0.1: 1850 | version "1.0.1" 1851 | resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz" 1852 | integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== 1853 | dependencies: 1854 | is-map "^2.0.1" 1855 | is-set "^2.0.1" 1856 | is-weakmap "^2.0.1" 1857 | is-weakset "^2.0.1" 1858 | 1859 | which-typed-array@^1.1.13: 1860 | version "1.1.14" 1861 | resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz" 1862 | integrity sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg== 1863 | dependencies: 1864 | available-typed-arrays "^1.0.6" 1865 | call-bind "^1.0.5" 1866 | for-each "^0.3.3" 1867 | gopd "^1.0.1" 1868 | has-tostringtag "^1.0.1" 1869 | 1870 | which@^2.0.1: 1871 | version "2.0.2" 1872 | resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" 1873 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 1874 | dependencies: 1875 | isexe "^2.0.0" 1876 | 1877 | win-release@^1.0.0: 1878 | version "1.1.1" 1879 | resolved "https://registry.npmjs.org/win-release/-/win-release-1.1.1.tgz" 1880 | integrity sha512-iCRnKVvGxOQdsKhcQId2PXV1vV3J/sDPXKA4Oe9+Eti2nb2ESEsYHRYls/UjoUW3bIc5ZDO8dTH50A/5iVN+bw== 1881 | dependencies: 1882 | semver "^5.0.1" 1883 | 1884 | wrappy@1: 1885 | version "1.0.2" 1886 | resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" 1887 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 1888 | 1889 | wyze-api@1.1.7: 1890 | version "1.1.7" 1891 | resolved "https://registry.npmjs.org/wyze-api/-/wyze-api-1.1.7.tgz" 1892 | integrity sha512-iay3OIgyx5zVpQMmKDXrwdbeec5srK6x1Ia3w2hkAYstpVKlAncI/yU27CDjgyd+uiDtXezPuD4Y3NDa5K050g== 1893 | dependencies: 1894 | "@ptkdev/logger" "1.8.0" 1895 | axios "1.5.0" 1896 | base64-js "1.5.1" 1897 | colorsys "^1.0.22" 1898 | crypto-js "4.1.1" 1899 | moment "2.29.4" 1900 | querystring "0.2.1" 1901 | urllib "3.18.0" 1902 | utf8 "3.0.0" 1903 | uuid-by-string "4.0.0" 1904 | 1905 | xml2js@^0.5.0: 1906 | version "0.5.0" 1907 | resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz" 1908 | integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA== 1909 | dependencies: 1910 | sax ">=0.6.0" 1911 | xmlbuilder "~11.0.0" 1912 | 1913 | xmlbuilder@~11.0.0: 1914 | version "11.0.1" 1915 | resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz" 1916 | integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== 1917 | 1918 | yallist@^4.0.0: 1919 | version "4.0.0" 1920 | resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" 1921 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 1922 | 1923 | ylru@^1.3.2: 1924 | version "1.3.2" 1925 | resolved "https://registry.npmjs.org/ylru/-/ylru-1.3.2.tgz" 1926 | integrity sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA== 1927 | 1928 | yocto-queue@^0.1.0: 1929 | version "0.1.0" 1930 | resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" 1931 | integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== 1932 | --------------------------------------------------------------------------------