diff --git a/.github/workflows/beta-release.yml b/.github/workflows/beta-release.yml
index df9fd87..d114344 100644
--- a/.github/workflows/beta-release.yml
+++ b/.github/workflows/beta-release.yml
@@ -3,6 +3,8 @@ name: Node-CI Beta
on:
push:
branches: [beta-*.*.*, beta]
+ release:
+ types: [prereleased]
workflow_dispatch:
jobs:
@@ -29,3 +31,15 @@ jobs:
pre_id: 'beta'
secrets:
npm_auth_token: ${{ secrets.npm_token }}
+
+ github-releases-to-discord:
+ needs: publish
+
+ if: ${{ github.repository == 'donavanbecker/homebridge-meater' && github.event.release.prerelease == true }}
+
+ uses: OpenWonderLabs/.github/.github/workflows/discord-webhooks.yml@latest
+ with:
+ footer_title: "August"
+ secrets:
+ DISCORD_WEBHOOK_URL_LATEST: ${{ secrets.DISCORD_WEBHOOK_URL_LATEST }}
+ DISCORD_WEBHOOK_URL_BETA: ${{ secrets.DISCORD_WEBHOOK_URL_BETA }}
diff --git a/.github/workflows/changerelease.yml b/.github/workflows/changerelease.yml
index e66082d..111ba97 100644
--- a/.github/workflows/changerelease.yml
+++ b/.github/workflows/changerelease.yml
@@ -1,13 +1,11 @@
name: Changelog to Release
on:
- workflow_dispatch:
- push:
- paths: [CHANGELOG.md]
- branches: [latest]
+ release:
+ types: [published]
jobs:
- changerelease:
- uses: donavanbecker/.github/.github/workflows/changerelease.yml@latest
- secrets:
- token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
+ changerelease:
+ uses: donavanbecker/.github/.github/workflows/changerelease.yml@latest
+ secrets:
+ token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml
index 568711a..525d2b2 100644
--- a/.github/workflows/dependabot.yml
+++ b/.github/workflows/dependabot.yml
@@ -2,16 +2,12 @@ name: AutoDependabot
on:
pull_request:
- branches:
- - beta
- - latest
+ branches: [ beta, latest ]
pull_request_target:
- branches:
- - beta
- - latest
+ branches: [ beta, latest ]
jobs:
- label:
+ dependabot:
uses: donavanbecker/.github/.github/workflows/dependabot.yml@latest
secrets:
token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/discord-webhooks.yml b/.github/workflows/discord-webhooks.yml
deleted file mode 100644
index 7037ba9..0000000
--- a/.github/workflows/discord-webhooks.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-# This is a basic workflow to help you get started with Actions
-
-name: Discord Webhooks
-
-# Controls when the workflow will run
-on:
- release:
- types: [released, prereleased]
-
-jobs:
- github-releases-to-discord:
- uses: donavanbecker/.github/.github/workflows/discord-webhooks.yml@latest
- with:
- footer_title: "Meater"
- secrets:
- DISCORD_WEBHOOK_URL_LATEST: ${{ secrets.DISCORD_WEBHOOK_URL_LATEST }}
- DISCORD_WEBHOOK_URL_BETA: ${{ secrets.DISCORD_WEBHOOK_URL_BETA }}
\ No newline at end of file
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
index d31b349..6eb3c01 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/labeler.yml
@@ -1,16 +1,9 @@
-# This workflow will triage pull requests and apply a label based on the
-# paths that are modified in the pull request.
-#
-# To use this workflow, you will need to set up a .github/labeler.yml
-# file with configuration. For more information, see:
-# https://github.com/actions/labeler/blob/main/README.md
-
name: Labeler
on: [pull_request]
jobs:
- label:
+ labeler:
uses: donavanbecker/.github/.github/workflows/labeler.yml@latest
secrets:
token: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml
index e521f3a..3754194 100644
--- a/.github/workflows/release-drafter.yml
+++ b/.github/workflows/release-drafter.yml
@@ -9,7 +9,7 @@ on:
workflow_dispatch:
jobs:
- stale:
+ release-drafter:
uses: donavanbecker/.github/.github/workflows/release-drafter.yml@latest
secrets:
token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 58e56a8..4aa6958 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -22,3 +22,15 @@ jobs:
uses: donavanbecker/.github/.github/workflows/npm-publish.yml@latest
secrets:
npm_auth_token: ${{ secrets.npm_token }}
+
+ github-releases-to-discord:
+ needs: publish
+
+ if: ${{ github.repository == 'donavanbecker/homebridge-meater' }}
+
+ uses: donavanbecker/.github/.github/workflows/discord-webhooks.yml@latest
+ with:
+ footer_title: "Meater"
+ secrets:
+ DISCORD_WEBHOOK_URL_LATEST: ${{ secrets.DISCORD_WEBHOOK_URL_LATEST }}
+ DISCORD_WEBHOOK_URL_BETA: ${{ secrets.DISCORD_WEBHOOK_URL_BETA }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 884815e..96516a0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,10 +2,17 @@
All notable changes to this project will be documented in this file. This project uses [Semantic Versioning](https://semver.org/)
-## [1.0.0](https://github.com/donavanbecker/homebridge-meater/releases/tag/v1.0.0) (2024-01-31)
+## [1.1.0](https://github.com/donavanbecker/homebridge-meater/releases/tag/v1.1.0) (2024-02-13)
### What's Changed
+- Added `device` config so that config can be assign on each device.
+- Housekeeping and updated dependencies.
+
+**Full Changelog**: https://github.com/donavanbecker/homebridge-meater/compare/v1.0.0...v1.1.0
+
+## [1.0.0](https://github.com/donavanbecker/homebridge-meater/releases/tag/v1.0.0) (2024-01-31)
+### What's Changed
- Release of [homebridge-meater](https://github.com/donavanbecker/homebridge-meater) which allows you to see the temperature to your Meater Thermometer throw HomeKit.
**Full Changelog**: https://github.com/donavanbecker/homebridge-meater/compare/v0.1.0...v1.0.0
@@ -13,5 +20,4 @@ All notable changes to this project will be documented in this file. This projec
## [0.1.0](https://github.com/donavanbecker/homebridge-meater/releases/tag/v0.1.0) (2024-01-12)
### What's Changed
-
- Initial release of homebridge-meater.
diff --git a/config.schema.json b/config.schema.json
index 80b9621..90fe95f 100644
--- a/config.schema.json
+++ b/config.schema.json
@@ -14,56 +14,216 @@
"title": "Name",
"default": "Meater"
},
- "email": {
- "type": "string",
- "title": "email",
- "placeholder": "apple@icloud.com",
- "format": "email"
- },
- "password": {
- "type": "string",
- "title": "Password",
- "x-schema-form": {
- "type": "password"
- }
- },
- "logging": {
- "title": "Logging Setting",
- "type": "string",
- "required": true,
- "default": "",
- "oneOf": [
- {
- "title": "Default Logging",
- "enum": [
- ""
- ]
+ "credentials": {
+ "type": "object",
+ "properties": {
+ "email": {
+ "type": "string",
+ "title": "email",
+ "placeholder": "apple@icloud.com",
+ "format": "email"
},
- {
- "title": "Standard Logging",
- "enum": [
- "standard"
- ]
+ "password": {
+ "type": "string",
+ "title": "Password",
+ "x-schema-form": {
+ "type": "password"
+ }
},
- {
- "title": "No Logging",
- "enum": [
- "none"
- ]
+ "token": {
+ "type": "string",
+ "title": "Token",
+ "x-schema-form": {
+ "type": "token"
+ }
+ },
+ "notice": {
+ "title": "Notice",
+ "type": "string",
+ "default": "Keep your tokens a secret!"
+ }
+ },
+ "required": ["email", "password"]
+ },
+ "options": {
+ "type": "object",
+ "properties": {
+ "devices": {
+ "type": "array",
+ "items": {
+ "title": "Devices",
+ "type": "object",
+ "properties": {
+ "id": {
+ "title": "Device ID",
+ "type": "string",
+ "placeholder": "81F3UT59513F"
+ },
+ "configDeviceName": {
+ "title": "Device Name",
+ "type": "string",
+ "placeholder": "Hallway Thermostat",
+ "condition": {
+ "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].id);"
+ }
+ },
+ "hide_device": {
+ "title": "Hide Device",
+ "type": "boolean",
+ "condition": {
+ "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].id);"
+ }
+ },
+ "external": {
+ "title": "External Accessory",
+ "type": "boolean",
+ "condition": {
+ "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].id && !model.options.devices[arrayIndices].hide_device);"
+ }
+ },
+ "firmware": {
+ "title": "Firmware Override",
+ "type": "string",
+ "placeholder": "1.2.8",
+ "condition": {
+ "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].id && !model.options.devices[arrayIndices].hide_device);"
+ }
+ },
+ "refreshRate": {
+ "title": "Device Refresh Rate",
+ "type": "number",
+ "minimum": 30,
+ "placeholder": 360,
+ "description": "Indicates the number of seconds between polls of Meater API.",
+ "condition": {
+ "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].id && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].deviceClass === 'Thermostat' || model.options.devices[arrayIndices].deviceClass === 'LeakDetector'));"
+ }
+ },
+ "logging": {
+ "title": "Device Logging Override Setting",
+ "type": "string",
+ "required": true,
+ "default": "",
+ "oneOf": [
+ {
+ "title": "Default Logging",
+ "enum": [""]
+ },
+ {
+ "title": "Standard Logging",
+ "enum": ["standard"]
+ },
+ {
+ "title": "No Logging",
+ "enum": ["none"]
+ },
+ {
+ "title": "Debug Logging",
+ "enum": ["debug"]
+ }
+ ],
+ "condition": {
+ "functionBody": "return (model.options && model.options.devices && model.options.devices[arrayIndices].id && !model.options.devices[arrayIndices].hide_device && (model.options.devices[arrayIndices].deviceClass === 'Thermostat' || model.options.devices[arrayIndices].deviceClass === 'LeakDetector'));"
+ }
+ }
+ },
+ "required": ["id", "configDeviceName", "logging"]
+ }
},
- {
- "title": "Debug Logging",
- "enum": [
- "debug"
+ "refreshRate": {
+ "title": "Refresh Rate",
+ "type": "number",
+ "minimum": 30,
+ "placeholder": 120,
+ "description": "Indicates the number of seconds between polls of the Meater service."
+ },
+ "logging": {
+ "title": "Logging Setting",
+ "type": "string",
+ "required": true,
+ "default": "",
+ "oneOf": [
+ {
+ "title": "Default Logging",
+ "enum": [""]
+ },
+ {
+ "title": "Standard Logging",
+ "enum": ["standard"]
+ },
+ {
+ "title": "No Logging",
+ "enum": ["none"]
+ },
+ {
+ "title": "Debug Logging",
+ "enum": ["debug"]
+ }
]
}
- ]
+ },
+ "required": ["logging"]
}
}
},
"layout": [
- "email",
- "password",
- "logging"
+ {
+ "type": "fieldset",
+ "title": "Meater Account Info",
+ "expandable": true,
+ "expanded": false,
+ "items": [
+ {
+ "type": "help",
+ "helpvalue": "
This is for Manual Setup Only."
+ },
+ "credentials.email",
+ "credentials.password",
+ "credentials.token"
+ ]
+ },
+ {
+ "type": "fieldset",
+ "title": "Meater Device Settings",
+ "expandable": true,
+ "expanded": false,
+ "items": [
+ {
+ "key": "options.devices",
+ "notitle": true,
+ "type": "tabarray",
+ "title": "{{ value.configDeviceName || value.id || 'New Meater Device' }}",
+ "expandable": true,
+ "expanded": false,
+ "orderable": false,
+ "items": [
+ "options.devices[].configDeviceName",
+ "options.devices[].id",
+ "options.devices[].hide_device",
+ "options.devices[].firmware",
+ "options.devices[].external",
+ "options.devices[].refreshRate",
+ "options.devices[].logging"
+ ]
+ }
+ ]
+ },
+ {
+ "type": "fieldset",
+ "title": "Advanced Settings",
+ "expandable": true,
+ "expanded": false,
+ "items": [
+ {
+ "type": "help",
+ "helpvalue": "Refresh Rate
Refresh Rate indicates the number of seconds between polls of the Meater service."
+ },
+ {
+ "key": "options.refreshRate",
+ "notitle": true
+ },
+ "options.logging"
+ ]
+ }
]
}
\ No newline at end of file
diff --git a/nodemon.json b/nodemon.json
index db352ce..758b873 100644
--- a/nodemon.json
+++ b/nodemon.json
@@ -1,8 +1,9 @@
{
"watch": [
- "src"
+ "src",
+ "config.schema.json"
],
- "ext": "ts",
+ "ext": "ts, html, json",
"ignore": [],
"exec": "DEBUG= tsc && homebridge -T -D -P -I -U ~/.homebridge-dev ..",
"signal": "SIGTERM",
diff --git a/package-lock.json b/package-lock.json
index 75a52d7..e275e33 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "homebridge-meater",
- "version": "1.0.0",
+ "version": "1.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "homebridge-meater",
- "version": "1.0.0",
+ "version": "1.1.0",
"funding": [
{
"type": "Paypal",
@@ -21,16 +21,16 @@
"dependencies": {
"@homebridge/plugin-ui-utils": "^1.0.1",
"rxjs": "^7.8.1",
- "undici": "^6.5.0"
+ "undici": "^6.6.2"
},
"devDependencies": {
- "@types/node": "^20.11.13",
- "@typescript-eslint/eslint-plugin": "^6.20.0",
- "@typescript-eslint/parser": "^6.20.0",
+ "@types/node": "^20.11.17",
+ "@typescript-eslint/eslint-plugin": "^7.0.1",
+ "@typescript-eslint/parser": "^7.0.1",
"eslint": "^8.56.0",
"homebridge": "^1.7.0",
"nodemon": "^3.0.3",
- "npm-check-updates": "^16.14.14",
+ "npm-check-updates": "^16.14.15",
"rimraf": "^5.0.5",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
@@ -810,31 +810,31 @@
"dev": true
},
"node_modules/@types/node": {
- "version": "20.11.13",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.13.tgz",
- "integrity": "sha512-5G4zQwdiQBSWYTDAH1ctw2eidqdhMJaNsiIDKHFr55ihz5Trl2qqR8fdrT732yPBho5gkNxXm67OxWFBqX9aPg==",
+ "version": "20.11.17",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz",
+ "integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/semver": {
- "version": "7.5.6",
- "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz",
- "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==",
+ "version": "7.5.7",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz",
+ "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==",
"dev": true
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "6.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.20.0.tgz",
- "integrity": "sha512-fTwGQUnjhoYHeSF6m5pWNkzmDDdsKELYrOBxhjMrofPqCkoC2k3B2wvGHFxa1CTIqkEn88nlW1HVMztjo2K8Hg==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.1.tgz",
+ "integrity": "sha512-OLvgeBv3vXlnnJGIAgCLYKjgMEU+wBGj07MQ/nxAaON+3mLzX7mJbhRYrVGiVvFiXtwFlkcBa/TtmglHy0UbzQ==",
"dev": true,
"dependencies": {
"@eslint-community/regexpp": "^4.5.1",
- "@typescript-eslint/scope-manager": "6.20.0",
- "@typescript-eslint/type-utils": "6.20.0",
- "@typescript-eslint/utils": "6.20.0",
- "@typescript-eslint/visitor-keys": "6.20.0",
+ "@typescript-eslint/scope-manager": "7.0.1",
+ "@typescript-eslint/type-utils": "7.0.1",
+ "@typescript-eslint/utils": "7.0.1",
+ "@typescript-eslint/visitor-keys": "7.0.1",
"debug": "^4.3.4",
"graphemer": "^1.4.0",
"ignore": "^5.2.4",
@@ -850,8 +850,8 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha",
- "eslint": "^7.0.0 || ^8.0.0"
+ "@typescript-eslint/parser": "^7.0.0",
+ "eslint": "^8.56.0"
},
"peerDependenciesMeta": {
"typescript": {
@@ -860,15 +860,15 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "6.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.20.0.tgz",
- "integrity": "sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.0.1.tgz",
+ "integrity": "sha512-8GcRRZNzaHxKzBPU3tKtFNing571/GwPBeCvmAUw0yBtfE2XVd0zFKJIMSWkHJcPQi0ekxjIts6L/rrZq5cxGQ==",
"dev": true,
"dependencies": {
- "@typescript-eslint/scope-manager": "6.20.0",
- "@typescript-eslint/types": "6.20.0",
- "@typescript-eslint/typescript-estree": "6.20.0",
- "@typescript-eslint/visitor-keys": "6.20.0",
+ "@typescript-eslint/scope-manager": "7.0.1",
+ "@typescript-eslint/types": "7.0.1",
+ "@typescript-eslint/typescript-estree": "7.0.1",
+ "@typescript-eslint/visitor-keys": "7.0.1",
"debug": "^4.3.4"
},
"engines": {
@@ -879,7 +879,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^7.0.0 || ^8.0.0"
+ "eslint": "^8.56.0"
},
"peerDependenciesMeta": {
"typescript": {
@@ -888,13 +888,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "6.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz",
- "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.1.tgz",
+ "integrity": "sha512-v7/T7As10g3bcWOOPAcbnMDuvctHzCFYCG/8R4bK4iYzdFqsZTbXGln0cZNVcwQcwewsYU2BJLay8j0/4zOk4w==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "6.20.0",
- "@typescript-eslint/visitor-keys": "6.20.0"
+ "@typescript-eslint/types": "7.0.1",
+ "@typescript-eslint/visitor-keys": "7.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
@@ -905,13 +905,13 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "6.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.20.0.tgz",
- "integrity": "sha512-qnSobiJQb1F5JjN0YDRPHruQTrX7ICsmltXhkV536mp4idGAYrIyr47zF/JmkJtEcAVnIz4gUYJ7gOZa6SmN4g==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.1.tgz",
+ "integrity": "sha512-YtT9UcstTG5Yqy4xtLiClm1ZpM/pWVGFnkAa90UfdkkZsR1eP2mR/1jbHeYp8Ay1l1JHPyGvoUYR6o3On5Nhmw==",
"dev": true,
"dependencies": {
- "@typescript-eslint/typescript-estree": "6.20.0",
- "@typescript-eslint/utils": "6.20.0",
+ "@typescript-eslint/typescript-estree": "7.0.1",
+ "@typescript-eslint/utils": "7.0.1",
"debug": "^4.3.4",
"ts-api-utils": "^1.0.1"
},
@@ -923,7 +923,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^7.0.0 || ^8.0.0"
+ "eslint": "^8.56.0"
},
"peerDependenciesMeta": {
"typescript": {
@@ -932,9 +932,9 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "6.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz",
- "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.1.tgz",
+ "integrity": "sha512-uJDfmirz4FHib6ENju/7cz9SdMSkeVvJDK3VcMFvf/hAShg8C74FW+06MaQPODHfDJp/z/zHfgawIJRjlu0RLg==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
@@ -945,13 +945,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "6.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz",
- "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.1.tgz",
+ "integrity": "sha512-SO9wHb6ph0/FN5OJxH4MiPscGah5wjOd0RRpaLvuBv9g8565Fgu0uMySFEPqwPHiQU90yzJ2FjRYKGrAhS1xig==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "6.20.0",
- "@typescript-eslint/visitor-keys": "6.20.0",
+ "@typescript-eslint/types": "7.0.1",
+ "@typescript-eslint/visitor-keys": "7.0.1",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -973,17 +973,17 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "6.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz",
- "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.1.tgz",
+ "integrity": "sha512-oe4his30JgPbnv+9Vef1h48jm0S6ft4mNwi9wj7bX10joGn07QRfqIqFHoMiajrtoU88cIhXf8ahwgrcbNLgPA==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
- "@typescript-eslint/scope-manager": "6.20.0",
- "@typescript-eslint/types": "6.20.0",
- "@typescript-eslint/typescript-estree": "6.20.0",
+ "@typescript-eslint/scope-manager": "7.0.1",
+ "@typescript-eslint/types": "7.0.1",
+ "@typescript-eslint/typescript-estree": "7.0.1",
"semver": "^7.5.4"
},
"engines": {
@@ -994,16 +994,16 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^7.0.0 || ^8.0.0"
+ "eslint": "^8.56.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "6.20.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz",
- "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.1.tgz",
+ "integrity": "sha512-hwAgrOyk++RTXrP4KzCg7zB2U0xt7RUU0ZdMSCsqF3eKUwkdXUMyTb0qdCuji7VIbcpG62kKTU9M1J1c9UpFBw==",
"dev": true,
"dependencies": {
- "@typescript-eslint/types": "6.20.0",
+ "@typescript-eslint/types": "7.0.1",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
@@ -1187,13 +1187,16 @@
"dev": true
},
"node_modules/array-buffer-byte-length": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
- "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz",
+ "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==",
"dev": true,
"dependencies": {
- "call-bind": "^1.0.2",
- "is-array-buffer": "^3.0.1"
+ "call-bind": "^1.0.5",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -1215,9 +1218,9 @@
}
},
"node_modules/available-typed-arrays": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
- "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz",
+ "integrity": "sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==",
"dev": true,
"engines": {
"node": ">= 0.4"
@@ -1446,14 +1449,19 @@
}
},
"node_modules/call-bind": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
- "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
+ "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"dev": true,
"dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
"function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.1",
- "set-function-length": "^1.1.1"
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -1497,16 +1505,10 @@
}
},
"node_modules/chokidar": {
- "version": "3.5.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
- "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
- "funding": [
- {
- "type": "individual",
- "url": "https://paulmillr.com/funding/"
- }
- ],
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -1519,6 +1521,9 @@
"engines": {
"node": ">= 8.10.0"
},
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
"optionalDependencies": {
"fsevents": "~2.3.2"
}
@@ -1826,17 +1831,20 @@
}
},
"node_modules/define-data-property": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
- "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dev": true,
"dependencies": {
- "get-intrinsic": "^1.2.1",
- "gopd": "^1.0.1",
- "has-property-descriptors": "^1.0.0"
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/define-properties": {
@@ -1965,6 +1973,27 @@
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
"dev": true
},
+ "node_modules/es-define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
+ "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/es-get-iterator": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
@@ -2256,9 +2285,9 @@
}
},
"node_modules/fastq": {
- "version": "1.17.0",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.0.tgz",
- "integrity": "sha512-zGygtijUMT7jnk3h26kUms3BkSDp4IfIKjmnqI2tvx6nuBfiF1UqOxbnLfzdv+apBy+53oaImsKtMw/xYbW+1w==",
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+ "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
"dev": true,
"dependencies": {
"reusify": "^1.0.4"
@@ -2538,16 +2567,20 @@
"dev": true
},
"node_modules/get-intrinsic": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
- "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
+ "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"dev": true,
"dependencies": {
+ "es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
+ "engines": {
+ "node": ">= 0.4"
+ },
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -2758,12 +2791,12 @@
}
},
"node_modules/has-property-descriptors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
- "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dev": true,
"dependencies": {
- "get-intrinsic": "^1.2.2"
+ "es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -2794,12 +2827,12 @@
}
},
"node_modules/has-tostringtag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
- "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"dev": true,
"dependencies": {
- "has-symbols": "^1.0.2"
+ "has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
@@ -2827,9 +2860,9 @@
}
},
"node_modules/hasown": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
- "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
+ "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.2"
@@ -2949,9 +2982,9 @@
}
},
"node_modules/ignore": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
- "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==",
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+ "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
"dev": true,
"engines": {
"node": ">= 4"
@@ -3050,12 +3083,12 @@
}
},
"node_modules/internal-slot": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz",
- "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==",
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz",
+ "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==",
"dev": true,
"dependencies": {
- "get-intrinsic": "^1.2.2",
+ "es-errors": "^1.3.0",
"hasown": "^2.0.0",
"side-channel": "^1.0.4"
},
@@ -3069,6 +3102,19 @@
"integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==",
"dev": true
},
+ "node_modules/ip-address": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
+ "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
+ "dev": true,
+ "dependencies": {
+ "jsbn": "1.1.0",
+ "sprintf-js": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/is-arguments": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
@@ -3086,14 +3132,16 @@
}
},
"node_modules/is-array-buffer": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
- "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz",
+ "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
- "get-intrinsic": "^1.2.0",
- "is-typed-array": "^1.1.10"
+ "get-intrinsic": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -3372,21 +3420,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-typed-array": {
- "version": "1.1.12",
- "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz",
- "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==",
- "dev": true,
- "dependencies": {
- "which-typed-array": "^1.1.11"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
@@ -3472,6 +3505,12 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/jsbn": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
+ "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
+ "dev": true
+ },
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -4404,9 +4443,9 @@
}
},
"node_modules/npm-check-updates": {
- "version": "16.14.14",
- "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.14.tgz",
- "integrity": "sha512-Y3ajS/Ep40jM489rLBdz9jehn/BMil5s9fA4PSr2ZJxxSmtLWCSmRqsI2IEZ9Nb3MTMu8a3s7kBs0l+JbjdkTA==",
+ "version": "16.14.15",
+ "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.15.tgz",
+ "integrity": "sha512-WH0wJ9j6CP7Azl+LLCxWAYqroT2IX02kRIzgK/fg0rPpMbETgHITWBdOPtrv521xmA3JMgeNsQ62zvVtS/nCmQ==",
"dev": true,
"dependencies": {
"chalk": "^5.3.0",
@@ -5149,14 +5188,15 @@
}
},
"node_modules/regexp.prototype.flags": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz",
- "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==",
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz",
+ "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==",
"dev": true,
"dependencies": {
- "call-bind": "^1.0.2",
- "define-properties": "^1.2.0",
- "set-function-name": "^2.0.0"
+ "call-bind": "^1.0.6",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "set-function-name": "^2.0.1"
},
"engines": {
"node": ">= 0.4"
@@ -5342,9 +5382,9 @@
"dev": true
},
"node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "version": "7.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+ "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
@@ -5396,14 +5436,15 @@
"dev": true
},
"node_modules/set-function-length": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz",
- "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz",
+ "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==",
"dev": true,
"dependencies": {
- "define-data-property": "^1.1.1",
+ "define-data-property": "^1.1.2",
+ "es-errors": "^1.3.0",
"function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.2",
+ "get-intrinsic": "^1.2.3",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.1"
},
@@ -5447,14 +5488,18 @@
}
},
"node_modules/side-channel": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
- "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz",
+ "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==",
"dev": true,
"dependencies": {
- "call-bind": "^1.0.0",
- "get-intrinsic": "^1.0.2",
- "object-inspect": "^1.9.0"
+ "call-bind": "^1.0.6",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.4",
+ "object-inspect": "^1.13.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -5529,16 +5574,16 @@
}
},
"node_modules/socks": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
- "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.3.tgz",
+ "integrity": "sha512-vfuYK48HXCTFD03G/1/zkIls3Ebr2YNa4qU9gHDZdblHLiqhJrJGkY3+0Nx0JpN9qBhJbVObc1CNciT1bIZJxw==",
"dev": true,
"dependencies": {
- "ip": "^2.0.0",
+ "ip-address": "^9.0.5",
"smart-buffer": "^4.2.0"
},
"engines": {
- "node": ">= 10.13.0",
+ "node": ">= 10.0.0",
"npm": ">= 3.0.0"
}
},
@@ -5556,12 +5601,6 @@
"node": ">= 10"
}
},
- "node_modules/socks/node_modules/ip": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
- "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==",
- "dev": true
- },
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -5620,9 +5659,9 @@
}
},
"node_modules/spdx-license-ids": {
- "version": "3.0.16",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz",
- "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==",
+ "version": "3.0.17",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz",
+ "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==",
"dev": true
},
"node_modules/split": {
@@ -5637,6 +5676,12 @@
"node": "*"
}
},
+ "node_modules/sprintf-js": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
+ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
+ "dev": true
+ },
"node_modules/ssri": {
"version": "10.0.5",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz",
@@ -5878,12 +5923,12 @@
}
},
"node_modules/ts-api-utils": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
- "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz",
+ "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==",
"dev": true,
"engines": {
- "node": ">=16.13.0"
+ "node": ">=16"
},
"peerDependencies": {
"typescript": ">=4.2.0"
@@ -6010,9 +6055,9 @@
"dev": true
},
"node_modules/undici": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-6.5.0.tgz",
- "integrity": "sha512-/MUmPb2ptTvp1j7lPvdMSofMdqPxcOhAaKZi4k55sqm6XMeKI3n1dZJ5cnD4gLjpt2l7CIlthR1IXM59xKhpxw==",
+ "version": "6.6.2",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.6.2.tgz",
+ "integrity": "sha512-vSqvUE5skSxQJ5sztTZ/CdeJb1Wq0Hf44hlYMciqHghvz+K88U0l7D6u1VsndoFgskDcnU+nG3gYmMzJVzd9Qg==",
"dependencies": {
"@fastify/busboy": "^2.0.0"
},
@@ -6213,16 +6258,16 @@
}
},
"node_modules/which-typed-array": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz",
- "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==",
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz",
+ "integrity": "sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==",
"dev": true,
"dependencies": {
- "available-typed-arrays": "^1.0.5",
- "call-bind": "^1.0.4",
+ "available-typed-arrays": "^1.0.6",
+ "call-bind": "^1.0.5",
"for-each": "^0.3.3",
"gopd": "^1.0.1",
- "has-tostringtag": "^1.0.0"
+ "has-tostringtag": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
diff --git a/package.json b/package.json
index 15fd356..268029d 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"displayName": "Meater",
"name": "homebridge-meater",
- "version": "1.0.0",
+ "version": "1.1.0",
"description": "The Meater plugin allows you to access your Meater device(s) from HomeKit.",
"author": {
"name": "donavanbecker",
@@ -25,15 +25,13 @@
"scripts": {
"check": "npm install && npm outdated",
"update": "ncu -u && npm update && npm install",
- "updateDependencies": "npm run check && npm run update",
- "lint": "eslint src/**.ts",
+ "lint": "eslint src/**/*.ts",
"watch": "npm run build && npm run plugin-ui && npm link && nodemon",
"plugin-ui": "rsync ./src/homebridge-ui/public/index.html ./dist/homebridge-ui/public/",
"build": "rimraf ./dist && tsc",
"prepublishOnly": "npm run lint && npm run build && npm run plugin-ui",
"postpublish": "npm run clean",
- "clean": "rimraf ./dist",
- "test": "eslint src/**.ts"
+ "clean": "rimraf ./dist"
},
"funding": [
{
@@ -56,16 +54,16 @@
"dependencies": {
"@homebridge/plugin-ui-utils": "^1.0.1",
"rxjs": "^7.8.1",
- "undici": "^6.5.0"
+ "undici": "^6.6.2"
},
"devDependencies": {
- "@types/node": "^20.11.13",
- "@typescript-eslint/eslint-plugin": "^6.20.0",
- "@typescript-eslint/parser": "^6.20.0",
+ "@types/node": "^20.11.17",
+ "@typescript-eslint/eslint-plugin": "^7.0.1",
+ "@typescript-eslint/parser": "^7.0.1",
"eslint": "^8.56.0",
"homebridge": "^1.7.0",
"nodemon": "^3.0.3",
- "npm-check-updates": "^16.14.14",
+ "npm-check-updates": "^16.14.15",
"rimraf": "^5.0.5",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
diff --git a/src/device/device.ts b/src/device/device.ts
new file mode 100644
index 0000000..8d6cdfb
--- /dev/null
+++ b/src/device/device.ts
@@ -0,0 +1,147 @@
+/* Copyright(C) 2023-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+ *
+ * device.ts: homebridge-meater.
+ */
+import { API, HAP, Logging, PlatformAccessory } from 'homebridge';
+
+import { MeaterPlatform } from '../platform.js';
+import { MeaterPlatformConfig, device, devicesConfig } from '../settings.js';
+
+export abstract class deviceBase {
+ public readonly api: API;
+ public readonly log: Logging;
+ public readonly config!: MeaterPlatformConfig;
+ protected readonly hap: HAP;
+
+ // Config
+ protected deviceLogging!: string;
+ protected deviceRefreshRate!: number;
+
+ constructor(
+ protected readonly platform: MeaterPlatform,
+ protected accessory: PlatformAccessory,
+ protected device: device,
+ ) {
+ this.api = this.platform.api;
+ this.log = this.platform.log;
+ this.config = this.platform.config;
+ this.hap = this.api.hap;
+
+ this.deviceLogs(device);
+ this.getDeviceRefreshRate(device);
+ this.deviceConfigOptions(device);
+ this.deviceContext(accessory, device);
+
+ // Set accessory information
+ accessory
+ .getService(this.hap.Service.AccessoryInformation)!
+ .setCharacteristic(this.hap.Characteristic.Manufacturer, 'Meater')
+ .setCharacteristic(this.hap.Characteristic.Model, 'Smart Meat Thermometer')
+ .setCharacteristic(this.hap.Characteristic.SerialNumber, device.id)
+ .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
+ }
+
+ async deviceLogs(device: device & devicesConfig): Promise {
+ if (this.platform.debugMode) {
+ this.deviceLogging = this.accessory.context.logging = 'debugMode';
+ this.debugWarnLog(`Lock: ${this.accessory.displayName} Using Debug Mode Logging: ${this.deviceLogging}`);
+ } else if (device.logging) {
+ this.deviceLogging = this.accessory.context.logging = device.logging;
+ this.debugWarnLog(`Lock: ${this.accessory.displayName} Using Device Config Logging: ${this.deviceLogging}`);
+ } else if (this.config.logging) {
+ this.deviceLogging = this.accessory.context.logging = this.config.logging;
+ this.debugWarnLog(`Lock: ${this.accessory.displayName} Using Platform Config Logging: ${this.deviceLogging}`);
+ } else {
+ this.deviceLogging = this.accessory.context.logging = 'standard';
+ this.debugWarnLog(`Lock: ${this.accessory.displayName} Logging Not Set, Using: ${this.deviceLogging}`);
+ }
+ }
+
+ async getDeviceRefreshRate(device: device & devicesConfig): Promise {
+ if (device.refreshRate) {
+ if (device.refreshRate < 1800) {
+ device.refreshRate = 1800;
+ this.warnLog('Refresh Rate cannot be set to lower the 5 mins, as Lock detail (battery level, etc) are unlikely to change within that period');
+ }
+ this.deviceRefreshRate = this.accessory.context.refreshRate = device.refreshRate;
+ this.debugLog(`Lock: ${this.accessory.displayName} Using Device Config refreshRate: ${this.deviceRefreshRate}`);
+ } else if (this.config.refreshRate) {
+ this.deviceRefreshRate = this.accessory.context.refreshRate = this.config.refreshRate;
+ this.debugLog(`Lock: ${this.accessory.displayName} Using Platform Config refreshRate: ${this.deviceRefreshRate}`);
+ }
+ }
+
+ async deviceConfigOptions(device: device & devicesConfig): Promise {
+ const deviceConfig = {};
+ if (device.logging !== undefined) {
+ deviceConfig['logging'] = device.logging;
+ }
+ if (device.refreshRate !== undefined) {
+ deviceConfig['refreshRate'] = device.refreshRate;
+ }
+ if (Object.entries(deviceConfig).length !== 0) {
+ this.infoLog(`Lock: ${this.accessory.displayName} Config: ${JSON.stringify(deviceConfig)}`);
+ }
+ }
+
+ async deviceContext(accessory: PlatformAccessory, device: device & devicesConfig): Promise {
+ if (device.firmware) {
+ accessory.context.FirmwareRevision = device.firmware;
+ } else if (accessory.context.FirmwareRevision === undefined) {
+ accessory.context.FirmwareRevision = await this.platform.getVersion();
+ } else {
+ accessory.context.FirmwareRevision = '3';
+ }
+ }
+
+ /**
+ * Logging for Device
+ */
+ infoLog(...log: any[]): void {
+ if (this.enablingDeviceLogging()) {
+ this.log.info(String(...log));
+ }
+ }
+
+ warnLog(...log: any[]): void {
+ if (this.enablingDeviceLogging()) {
+ this.log.warn(String(...log));
+ }
+ }
+
+ debugWarnLog(...log: any[]): void {
+ if (this.enablingDeviceLogging()) {
+ if (this.deviceLogging?.includes('debug')) {
+ this.log.warn('[DEBUG]', String(...log));
+ }
+ }
+ }
+
+ errorLog(...log: any[]): void {
+ if (this.enablingDeviceLogging()) {
+ this.log.error(String(...log));
+ }
+ }
+
+ debugErrorLog(...log: any[]): void {
+ if (this.enablingDeviceLogging()) {
+ if (this.deviceLogging?.includes('debug')) {
+ this.log.error('[DEBUG]', String(...log));
+ }
+ }
+ }
+
+ debugLog(...log: any[]): void {
+ if (this.enablingDeviceLogging()) {
+ if (this.deviceLogging === 'debug') {
+ this.log.info('[DEBUG]', String(...log));
+ } else {
+ this.log.debug(String(...log));
+ }
+ }
+ }
+
+ enablingDeviceLogging(): boolean {
+ return this.deviceLogging.includes('debug') || this.deviceLogging === 'standard';
+ }
+}
\ No newline at end of file
diff --git a/src/device/meater.ts b/src/device/meater.ts
index 0ecaa92..af352ee 100644
--- a/src/device/meater.ts
+++ b/src/device/meater.ts
@@ -1,158 +1,162 @@
-import { Service, PlatformAccessory, CharacteristicValue, API, HAP, Logging } from 'homebridge';
-import { MeaterPlatform } from '../platform.js';
-import { interval } from 'rxjs';
+/* Copyright(C) 2023-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+ *
+ * meater.ts: homebridge-meater.
+ */
+import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge';
import { request } from 'undici';
-import { MeaterPlatformConfig, device, meaterUrl } from '../settings.js';
+import { interval, skipWhile } from 'rxjs';
+
+import { deviceBase } from './device.js';
+import { MeaterPlatform } from '../platform.js';
+import { device, devicesConfig, meaterUrl } from '../settings.js';
/**
* Platform Accessory
* An instance of this class is created for each accessory your platform registers
* Each accessory may expose multiple services of different service types.
*/
-export class Meater {
- public readonly api: API;
- public readonly log: Logging;
- public readonly config!: MeaterPlatformConfig;
- protected readonly hap: HAP;
- // Services
- serviceLabel!: Service;
- cookRefreshSwitchService!: Service;
- internalTemperatureService!: Service;
- ambientTemperatureService!: Service;
-
- // Characteristic Values
- internalCurrentTemperature: CharacteristicValue;
- ambientCurrentTemperature: CharacteristicValue;
+export class Meater extends deviceBase {
+ // Service
+ private serviceLabel!: {
+ service: Service;
+ };
+
+ private cookRefresh!: {
+ service: Service;
+ on: CharacteristicValue;
+ };
+
+ private internal!: {
+ service: Service;
+ currentTemperature: CharacteristicValue;
+ };
+
+ private ambient!: {
+ service: Service;
+ currentTemperature: CharacteristicValue;
+ };
// Cofiguration
- cookRefresh!: boolean;
+ CookRefresh!: boolean;
// Updates
SensorUpdateInProgress!: boolean;
constructor(
- private readonly platform: MeaterPlatform,
- private readonly accessory: PlatformAccessory,
- public device: device,
+ readonly platform: MeaterPlatform,
+ accessory: PlatformAccessory,
+ device: device & devicesConfig,
) {
- this.api = this.platform.api;
- this.log = this.platform.log;
- this.config = this.platform.config;
- this.hap = this.api.hap;
-
- this.cookRefresh = accessory.context.cookRefresh;
- this.internalCurrentTemperature = accessory.context.internalCurrentTemperature;
- this.ambientCurrentTemperature = accessory.context.ambientCurrentTemperature;
- accessory.context.FirmwareRevision = 'v1.0.0';
-
- // Retrieve initial values and updateHomekit
- this.refreshStatus();
-
- // set accessory information
- accessory
- .getService(this.hap.Service.AccessoryInformation)!
- .setCharacteristic(this.hap.Characteristic.Manufacturer, 'Meater')
- .setCharacteristic(this.hap.Characteristic.Model, 'Smart Meat Thermometer')
- .setCharacteristic(this.hap.Characteristic.SerialNumber, device.id)
- .setCharacteristic(this.hap.Characteristic.FirmwareRevision, accessory.context.FirmwareRevision);
-
- // Service Label Service
- this.serviceLabel = this.accessory.getService(this.hap.Service.ServiceLabel) ||
- this.accessory.addService(this.hap.Service.ServiceLabel, `Meater Thermometer (${device.id.slice(0, 4)})` );
- this.serviceLabel.setCharacteristic(this.hap.Characteristic.Name, `Meater Thermometer (${device.id.slice(0, 4)})`);
- if (
- !this.serviceLabel.testCharacteristic(this.hap.Characteristic.ConfiguredName) &&
- !this.serviceLabel.testCharacteristic(this.hap.Characteristic.Name)
- ) {
- this.serviceLabel.addCharacteristic(
- this.hap.Characteristic.ConfiguredName, `Meater Thermometer (${device.id.slice(0, 4)})`,
- );
- }
+ super(platform, accessory, device);
- // Interal Temperature Sensor Service
- this.internalTemperatureService = this.accessory.getServiceById(this.hap.Service.TemperatureSensor, 'Internal Temperature');
- if (!this.internalTemperatureService) {
- this.internalTemperatureService = new this.hap.Service.TemperatureSensor('Internal Temperature', 'Internal Temperature');
- if (this.internalTemperatureService) {
- this.internalTemperatureService = this.accessory.addService(this.internalTemperatureService);
- this.log.debug('Internal Temperature Service');
- } else {
- this.log.error('Internal Temperature Service -- Failed!');
- }
- }
- this.internalTemperatureService.setCharacteristic(this.hap.Characteristic.Name, 'Internal Temperature');
- if (!this.internalTemperatureService.testCharacteristic(this.hap.Characteristic.ConfiguredName) &&
- !this.internalTemperatureService.testCharacteristic(this.hap.Characteristic.Name)) {
- this.internalTemperatureService.addCharacteristic(this.hap.Characteristic.ConfiguredName, 'Internal Temperature');
- }
+ this.deviceContext();
+
+ // serviceLabel Service
+ this.debugLog('Configure serviceLabel Service');
+ this.serviceLabel = {
+ service: this.accessory.getService(this.hap.Service.ServiceLabel)
+ ?? this.accessory.addService(this.hap.Service.ServiceLabel, device.configDeviceName || `Meater Thermometer (${device.id.slice(0, 4)})`),
+ };
+
+ // Add serviceLabel Service's Characteristics
+ this.serviceLabel.service
+ .setCharacteristic(this.hap.Characteristic.Name, device.configDeviceName || `Meater Thermometer (${device.id.slice(0, 4)})`);
+
+ // InternalTemperature Senosr Service
+ this.debugLog('Configure InternalTemperature Service');
+ this.internal = {
+ service: this.accessory.getServiceById(this.hap.Service.TemperatureSensor, 'Internal Temperature'),
+ currentTemperature: this.accessory.context.internalCurrentTemperature,
+ };
- this.ambientTemperatureService = this.accessory.getServiceById(this.hap.Service.TemperatureSensor, 'Ambient Temperature');
- if (!this.ambientTemperatureService) {
- this.ambientTemperatureService = new this.hap.Service.TemperatureSensor('Ambient Temperature', 'Ambient Temperature');
- if (this.ambientTemperatureService) {
- this.ambientTemperatureService = this.accessory.addService(this.ambientTemperatureService);
- this.log.debug('Ambient Temperature Service');
- } else {
- this.log.error('Ambient Temperature Service -- Failed!');
+ if (this.internal) {
+ if (!this.internal.service) {
+ this.internal.service = new this.hap.Service.TemperatureSensor('Internal Temperature', 'Internal Temperature');
+ if (this.internal.service) {
+ this.internal.service = this.accessory.addService(this.internal.service);
+ this.log.debug('Internal Temperature Service');
+ } else {
+ this.log.error('Internal Temperature Service -- Failed!');
+ }
}
}
- this.ambientTemperatureService.setCharacteristic(this.hap.Characteristic.Name, 'Ambient Temperature');
- if (!this.ambientTemperatureService.testCharacteristic(this.hap.Characteristic.ConfiguredName) &&
- !this.ambientTemperatureService.testCharacteristic(this.hap.Characteristic.Name)) {
- this.ambientTemperatureService.addCharacteristic(
- this.hap.Characteristic.ConfiguredName, 'Ambient Temperature');
- }
- // Cook Refresh Switch Service Service
- this.cookRefreshSwitchService = this.accessory.getServiceById(this.hap.Service.Switch, 'Cook Refresh');
- if (!this.cookRefreshSwitchService) {
- this.cookRefreshSwitchService = new this.hap.Service.Switch('Cook Refresh', 'Cook Refresh');
- if (this.cookRefreshSwitchService) {
- this.cookRefreshSwitchService = this.accessory.addService(this.cookRefreshSwitchService);
- this.log.debug('Ambient Temperature Service');
- } else {
- this.log.error('Ambient Temperature Service -- Failed!');
+ // Add InternalTemperature Sensor Service's Characteristics
+ this.internal.service
+ .setCharacteristic(this.hap.Characteristic.Name, 'Internal Temperature')
+ .setCharacteristic(this.hap.Characteristic.ConfiguredName, 'Internal Temperature')
+ .setCharacteristic(this.hap.Characteristic.CurrentTemperature, this.internal.currentTemperature);
+
+ // AmbientTemperature Senosr Service
+ this.debugLog('Configure AmbientTemperature Service');
+ this.ambient = {
+ service: this.accessory.getServiceById(this.hap.Service.TemperatureSensor, 'Ambient Temperature'),
+ currentTemperature: this.accessory.context.ambientCurrentTemperature,
+ };
+ if (this.ambient) {
+ if (!this.ambient.service) {
+ this.ambient.service = new this.hap.Service.TemperatureSensor('Ambient Temperature', 'Ambient Temperature');
+ if (this.ambient.service) {
+ this.ambient.service = this.accessory.addService(this.ambient.service);
+ this.log.debug('Ambient Temperature Service');
+ } else {
+ this.log.error('Ambient Temperature Service -- Failed!');
+ }
}
}
- this.cookRefreshSwitchService.setCharacteristic(this.hap.Characteristic.Name, 'Cook Refresh');
- if (!this.cookRefreshSwitchService.testCharacteristic(this.hap.Characteristic.ConfiguredName) &&
- !this.cookRefreshSwitchService.testCharacteristic(this.hap.Characteristic.Name)) {
- this.cookRefreshSwitchService.addCharacteristic(
- this.hap.Characteristic.ConfiguredName, 'Cook Refresh');
- }
- // create handlers for required characteristics
- this.cookRefreshSwitchService.getCharacteristic(this.hap.Characteristic.On).onSet(this.handleOnSet.bind(this));
+
+ // Add AmbientTemperature Senosr Service's Characteristics
+ this.ambient.service
+ .setCharacteristic(this.hap.Characteristic.Name, 'Ambient Temperature')
+ .setCharacteristic(this.hap.Characteristic.ConfiguredName, 'Ambient Temperature')
+ .setCharacteristic(this.hap.Characteristic.CurrentTemperature, this.ambient.currentTemperature);
+
+ // cookRefresh Service
+ this.debugLog('Configure cookRefresh Service');
+ this.cookRefresh = {
+ service: this.accessory.getService(this.hap.Service.Switch) ?? this.accessory.addService(this.hap.Service.Switch, 'Cook Refresh'),
+ on: this.accessory.context.cookRefreshOn,
+ };
+
+ // Add serviceLabel Service's Characteristics
+ this.cookRefresh.service
+ .setCharacteristic(this.hap.Characteristic.Name, 'Cook Refresh')
+ .setCharacteristic(this.hap.Characteristic.ConfiguredName, 'Cook Refresh')
+ .setCharacteristic(this.hap.Characteristic.On, this.cookRefresh.on);
+
+ // Create handlers for required characteristics
+ this.cookRefresh.service
+ .getCharacteristic(this.hap.Characteristic.On)
+ .onSet(this.handleOnSet.bind(this));
+
+ // this is subject we use to track when we need to POST changes to the NoIP API
+ this.SensorUpdateInProgress = false;
// Retrieve initial values and update Homekit
this.updateHomeKitCharacteristics();
// Start an update interval
- (async () => {
- interval(await this.refreshRate() * 1000)
- .subscribe(async () => {
- await this.refreshStatus();
- });
- })();
- }
-
- async refreshRate() {
- return this.platform.config.refreshRate || 60;
+ interval(this.deviceRefreshRate * 1000)
+ .pipe(skipWhile(() => this.SensorUpdateInProgress))
+ .subscribe(async () => {
+ await this.refreshStatus();
+ });
}
/**
* Parse the device status from the SwitchBot api
*/
- async parseStatus(): Promise {
+ async parseStatus(device: device & devicesConfig): Promise {
// Internal Temperature
- this.internalCurrentTemperature = this.internalCurrentTemperature!;
- if (this.internalCurrentTemperature !== this.accessory.context.internalCurrentTemperature) {
- this.log.debug(`${this.accessory.displayName} Internal Current Temperature: ${this.internalCurrentTemperature}°c`);
+ this.internal.currentTemperature = device.data.temperature.internal;
+ if (this.internal.currentTemperature !== this.accessory.context.internalCurrentTemperature) {
+ this.log.debug(`${this.accessory.displayName} Internal Current Temperature: ${this.internal.currentTemperature}°c`);
}
// Ambient Temperature
- this.ambientCurrentTemperature = this.ambientCurrentTemperature!;
- if (this.ambientCurrentTemperature !== this.accessory.context.ambientCurrentTemperature) {
- this.log.debug(`${this.accessory.displayName} Ambient Current Temperature: ${this.ambientCurrentTemperature}°c`);
+ this.ambient.currentTemperature = device.data.temperature.ambient;
+ if (this.ambient.currentTemperature !== this.accessory.context.ambientCurrentTemperature) {
+ this.log.debug(`${this.accessory.displayName} Ambient Current Temperature: ${this.ambient.currentTemperature}°c`);
}
}
@@ -160,28 +164,27 @@ export class Meater {
* Asks the SwitchBot API for the latest device information
*/
async refreshStatus(): Promise {
- this.log.info(`Refreshing ${this.accessory.displayName} Status... Cooking: ${this.cookRefresh}`);
- if (this.cookRefresh) {
+ this.log.info(`Refreshing ${this.accessory.displayName} Status... Cooking: ${this.CookRefresh ? 'On' : 'Off'}`);
+ if (this.CookRefresh) {
try {
- if (this.config.token) {
- const { body, statusCode, headers } = await request(`${meaterUrl}/${this.device.id}`, {
+ if (this.config.credentials?.token) {
+ const { body, statusCode } = await request(`${meaterUrl}/${this.device.id}`, {
method: 'GET',
headers: {
- 'Authorization': 'Bearer ' + this.config.token,
+ 'Authorization': 'Bearer ' + this.config.credentials?.token,
},
});
- this.log.debug(`Device body: ${JSON.stringify(body)}`);
this.log.debug(`Device statusCode: ${statusCode}`);
- this.log.debug(`Device headers: ${JSON.stringify(headers)}`);
const device: any = await body.json();
this.log.debug(`Device: ${JSON.stringify(device)}`);
this.log.debug(`Device StatusCode: ${device.statusCode}`);
this.log.warn(`Device: ${JSON.stringify(device.data)}`);
if (statusCode === 200 && device.statusCode === 200) {
- this.internalCurrentTemperature = device.data.temperature.internal;
- this.ambientCurrentTemperature = device.data.temperature.ambient;
- this.cookRefresh = true;
- this.log.info(`${this.accessory.displayName} Internal: ${this.internalCurrentTemperature}, Ambient: ${this.ambientCurrentTemperature}°c`);
+ this.CookRefresh = true;
+ await this.parseStatus(device);
+ await this.updateHomeKitCharacteristics();
+ this.log.info(`${this.accessory.displayName} Internal: ${this.internal.currentTemperature}, `
+ + `Ambient: ${this.ambient.currentTemperature}°c`);
} else {
await this.statusCode(statusCode);
await this.statusCode(device.statusCode);
@@ -195,38 +198,36 @@ export class Meater {
}
} else {
this.log.info(`Cook Refresh is off for ${this.accessory.displayName}`);
- this.cookRefresh = false;
+ this.CookRefresh = false;
}
- await this.parseStatus();
- await this.updateHomeKitCharacteristics();
}
/**
* Updates the status for each of the HomeKit Characteristics
*/
async updateHomeKitCharacteristics(): Promise {
- if (this.internalCurrentTemperature === undefined) {
- this.log.debug(`${this.accessory.displayName} Internal Current Temperature: ${this.internalCurrentTemperature}`);
+ if (this.internal.currentTemperature === undefined) {
+ this.log.debug(`${this.accessory.displayName} Internal Current Temperature: ${this.internal.currentTemperature}`);
} else {
- this.accessory.context.internalCurrentTemperature = this.internalCurrentTemperature;
- this.internalTemperatureService?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.internalCurrentTemperature);
- this.log.debug(`${this.accessory.displayName} updateCharacteristic Internal Current Temperature: ${this.internalCurrentTemperature}`);
+ this.internal.service.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.internal.currentTemperature);
+ this.log.debug(`${this.accessory.displayName} updateCharacteristic Internal Current Temperature: ${this.internal.currentTemperature}`);
+ this.accessory.context.internalCurrentTemperature = this.internal.currentTemperature;
}
- if (this.ambientCurrentTemperature === undefined) {
- this.log.debug(`${this.accessory.displayName} Ambient Current Temperature: ${this.ambientCurrentTemperature}`);
+ if (this.ambient.currentTemperature === undefined) {
+ this.log.debug(`${this.accessory.displayName} Ambient Current Temperature: ${this.ambient.currentTemperature}`);
} else {
- this.accessory.context.ambientCurrentTemperature = this.ambientCurrentTemperature;
- this.ambientTemperatureService?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.ambientCurrentTemperature);
- this.log.debug(`${this.accessory.displayName} updateCharacteristic Ambient Current Temperature: ${this.ambientCurrentTemperature}`);
+ this.ambient.service?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, this.ambient.currentTemperature);
+ this.log.debug(`${this.accessory.displayName} updateCharacteristic Ambient Current Temperature: ${this.ambient.currentTemperature}`);
+ this.accessory.context.ambientCurrentTemperature = this.ambient.currentTemperature;
}
- if (this.cookRefresh === undefined) {
- this.log.debug(`${this.accessory.displayName} Cook Refresh Switch: ${this.cookRefresh}`);
+ if (this.cookRefresh.on === undefined) {
+ this.log.debug(`${this.accessory.displayName} Cook Refresh Switch: ${this.cookRefresh.on}`);
} else {
- this.accessory.context.cookRefresh = this.cookRefresh;
- this.cookRefreshSwitchService?.updateCharacteristic(this.hap.Characteristic.On, this.cookRefresh);
- this.log.debug(`${this.accessory.displayName} updateCharacteristic Cook Refresh Switch: ${this.cookRefresh}`);
+ this.accessory.context.cookRefreshOn = this.cookRefresh.on;
+ this.cookRefresh.service?.updateCharacteristic(this.hap.Characteristic.On, this.cookRefresh.on);
+ this.log.debug(`${this.accessory.displayName} updateCharacteristic Cook Refresh Switch: ${this.cookRefresh.on}`);
}
}
@@ -252,7 +253,7 @@ export class Meater {
break;
case 404:
this.log.error(`${this.accessory.displayName} Not Found, statusCode: ${statusCode}`);
- this.cookRefresh = false;
+ this.CookRefresh = false;
break;
case 429:
this.log.error(`${this.accessory.displayName} Too Many Requests, statusCode: ${statusCode}`);
@@ -267,7 +268,7 @@ export class Meater {
}
async apiError(e: any): Promise {
- this.internalTemperatureService?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e);
+ this.internal.service?.updateCharacteristic(this.hap.Characteristic.CurrentTemperature, e);
}
@@ -276,8 +277,26 @@ export class Meater {
*/
async handleOnSet(value: CharacteristicValue) {
this.log.info('Cook Refresh On:', value);
- this.cookRefresh = value as boolean;
+ this.CookRefresh = value as boolean;
await this.refreshStatus();
await this.updateHomeKitCharacteristics();
}
+
+ async deviceContext() {
+ if (this.accessory.context.internalCurrentTemperature === undefined) {
+ this.accessory.context.internalCurrentTemperature = 0;
+ } else {
+ this.accessory.context.internalCurrentTemperature;
+ }
+ if (this.accessory.context.ambientCurrentTemperature === undefined) {
+ this.accessory.context.ambientCurrentTemperature = 0;
+ } else {
+ this.accessory.context.ambientCurrentTemperature;
+ }
+ if (this.accessory.context.cookRefreshOn === undefined) {
+ this.accessory.context.cookRefreshOn = true;
+ } else {
+ this.accessory.context.cookRefreshOn;
+ }
+ }
}
diff --git a/src/homebridge-ui/public/index.html b/src/homebridge-ui/public/index.html
index 9ce9d37..211275f 100644
--- a/src/homebridge-ui/public/index.html
+++ b/src/homebridge-ui/public/index.html
@@ -52,23 +52,11 @@
Device ID |
- |
-
-
- Model |
- |
+ |
Firmware Version |
- |
-
-
- Device Type |
- |
-
-
- Connection Type |
- |
+ |
@@ -160,11 +148,8 @@ Help/About
const thisAcc = cachedAccessories.find((x) => x.UUID === UUID);
const context = thisAcc.context;
document.getElementById('displayName').innerHTML = thisAcc.displayName;
- document.getElementById('deviceID').innerHTML = context.deviceID;
- document.getElementById('model').innerHTML = context.model;
- document.getElementById('firmwareRevision').innerHTML = context.firmwareRevision || 'N/A';
- document.getElementById('deviceType').innerHTML = context.deviceType;
- document.getElementById('connectionType').innerHTML = context.connectionType;
+ document.getElementById('ID').innerHTML = context.device.id;
+ document.getElementById('FirmwareRevision').innerHTML = context.FirmwareRevision || 'N/A';
document.getElementById('deviceTable').style.display = 'inline-table';
homebridge.hideSpinner();
};
diff --git a/src/homebridge-ui/server.ts b/src/homebridge-ui/server.ts
index 92a2afc..05d38eb 100644
--- a/src/homebridge-ui/server.ts
+++ b/src/homebridge-ui/server.ts
@@ -1,4 +1,7 @@
-/* eslint-disable no-console */
+/* Copyright(C) 2023-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+ *
+ * server.ts: homebridge-meater.
+ */
import { HomebridgePluginUiServer } from '@homebridge/plugin-ui-utils';
import fs from 'fs';
diff --git a/src/index.ts b/src/index.ts
index 0686ced..ccde8d2 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,10 +1,11 @@
-/* Copyright(C) 2022-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+/* Copyright(C) 2023-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
*
- * index.ts: homebridge-august plugin registration.
+ * index.ts: homebridge-meater.
*/
-import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js';
import { API } from 'homebridge';
+
import { MeaterPlatform } from './platform.js';
+import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js';
// Register our platform with homebridge.
export default (api: API): void => {
diff --git a/src/platform.ts b/src/platform.ts
index 5cddc7f..990a9a6 100644
--- a/src/platform.ts
+++ b/src/platform.ts
@@ -1,13 +1,13 @@
-/* Copyright(C) 2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+/* Copyright(C) 2023-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
*
- * protect-platform.ts: homebridge-meater platform class.
+ * platform.ts: homebridge-meater.
*/
-import { API, DynamicPlatformPlugin, Logging, PlatformAccessory } from 'homebridge';
-import { PLATFORM_NAME, PLUGIN_NAME, MeaterPlatformConfig, meaterUrlLogin, meaterUrl, device, deviceConfig } from './settings.js';
+import { API, DynamicPlatformPlugin, HAP, Logging, PlatformAccessory } from 'homebridge';
+import { readFileSync, writeFileSync } from 'fs';
import { request } from 'undici';
+
import { Meater } from './device/meater.js';
-import { readFileSync, writeFileSync } from 'fs';
-import util from 'node:util';
+import { PLATFORM_NAME, PLUGIN_NAME, MeaterPlatformConfig, meaterUrlLogin, meaterUrl, device, devicesConfig } from './settings.js';
/**
* HomebridgePlatform
@@ -18,19 +18,22 @@ export class MeaterPlatform implements DynamicPlatformPlugin {
public accessories: PlatformAccessory[];
public readonly api: API;
public readonly log: Logging;
+ protected readonly hap: HAP;
public config!: MeaterPlatformConfig;
- public platformLogging!: string;
- public debugMode!: boolean;
+ platformConfig!: MeaterPlatformConfig['options'];
+ platformLogging!: MeaterPlatformConfig['logging'];
+ debugMode!: boolean;
- constructor(log: Logging, config: MeaterPlatformConfig, api: API) {
+ constructor(
+ log: Logging,
+ config: MeaterPlatformConfig,
+ api: API,
+ ) {
this.accessories = [];
this.api = api;
+ this.hap = this.api.hap;
this.log = log;
- //this.log.info = this.info.bind(this);
- //this.log.warn = this.warn.bind(this) || this.debugWarn.bind(this);
- //this.log.error = this.error.bind(this) || this.debugError.bind(this);
- //this.log.debug = this.debug.bind(this);
// only load if configured
if (!config) {
return;
@@ -38,62 +41,48 @@ export class MeaterPlatform implements DynamicPlatformPlugin {
// Plugin options into our config variables.
this.config = {
- platform: 'MeaterPlatform',
- email: config.email as string,
- password: config.password as string,
- token: config.token as string,
- logging: config.logging as string,
+ platform: 'Meater',
+ credentials: config.credentials,
+ options: config.options,
};
- this.logType();
- this.info((`Finished initializing platform: ${config.name}`));
+ this.platformConfigOptions();
+ this.platformLogs();
+ this.debugLog(`Finished initializing platform: ${config.name}`);
- this.debug('Debug logging on. Expect a lot of data.');
// verify the config
- try {
- this.verifyConfig();
- this.log.debug('Config OK');
- } catch (e: any) {
- this.log.error(`Verify Config, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://bit.ly/homebridge-meater-bug-report');
- this.log.error(`Verify Config, Error: ${e}`);
- return;
- }
+ (async () => {
+ try {
+ await this.verifyConfig();
+ this.debugLog('Config OK');
+ } catch (e: any) {
+ this.errorLog(`Verify Config, Error Message: ${e.message}, Submit Bugs Here: https://bit.ly/homebridge-meater-bug-report`);
+ this.debugErrorLog(`Verify Config, Error: ${e}`);
+ return;
+ }
+ })();
// When this event is fired it means Homebridge has restored all cached accessories from disk.
// Dynamic Platform plugins should only register new accessories after this event was fired,
// in order to ensure they weren't added to homebridge already. This event can also be used
// to start discovery of new accessories.
this.api.on('didFinishLaunching', async () => {
- this.log.debug('Executed didFinishLaunching callback');
+ this.debugLog('Executed didFinishLaunching callback');
// run the method to discover / register your devices as accessories
try {
- this.discoverDevices();
+ await this.discoverDevices();
} catch (e: any) {
- this.log.error(`Failed to Discover, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://bit.ly/homebridge-meater-bug-report');
- this.log.error(`Failed to Discover, Error: ${e}`);
+ this.errorLog(`Failed to Discover, Error Message: ${e.message}, Submit Bugs Here: ` + 'https://bit.ly/homebridge-meater-bug-report');
+ this.debugErrorLog(`Failed to Discover, Error: ${e}`);
}
});
}
- logType() {
- this.debugMode = process.argv.includes('-D') || process.argv.includes('--debug');
- if (this.config.logging === 'debug' || this.config.logging === 'standard' || this.config.logging === 'none') {
- this.platformLogging = this.config.options!.logging;
- this.log.warn(`Using Config Logging: ${this.platformLogging}`);
- } else if (this.debugMode) {
- this.platformLogging = 'debugMode';
- this.log.warn(`Using ${this.platformLogging} Logging`);
- } else {
- this.platformLogging = 'standard';
- this.log.warn(`Using ${this.platformLogging} Logging`);
- }
- }
-
/**
* This function is invoked when homebridge restores cached accessories from disk at startup.
* It should be used to setup event handlers for characteristics and update respective values.
*/
configureAccessory(accessory: PlatformAccessory) {
- this.log.debug(`Loading accessory from cache: ${accessory.displayName}`);
+ this.debugLog(`Loading accessory from cache: ${accessory.displayName}`);
// add the restored accessory to the accessories cache so we can track if it has already been registered
this.accessories.push(accessory);
@@ -103,10 +92,10 @@ export class MeaterPlatform implements DynamicPlatformPlugin {
* Verify the config passed to the plugin is valid
*/
async verifyConfig() {
- if (!this.config.email) {
+ if (!this.config.credentials?.email) {
throw new Error('Email not provided');
}
- if (!this.config.password) {
+ if (!this.config.credentials?.password) {
throw new Error('Password not provided');
}
}
@@ -114,12 +103,12 @@ export class MeaterPlatform implements DynamicPlatformPlugin {
/**
* The openToken was old config.
* This method saves the openToken as the token in the config.json file
- * @param this.config.credentials.openToken
+ * @param this.config.credentials.token
*/
- async updateToken() {
+ async updateToken(login: { data: { token: any; }; }) {
try {
// check the new token was provided
- if (!this.config.token) {
+ if (!this.config.credentials?.token) {
throw new Error('New token not provided');
}
@@ -138,16 +127,21 @@ export class MeaterPlatform implements DynamicPlatformPlugin {
throw new Error(`Cannot find config for ${PLATFORM_NAME} in platforms array`);
}
+ // check the .credentials is an object before doing object things with it
+ if (typeof pluginConfig.credentials !== 'object') {
+ throw new Error('pluginConfig.credentials is not an object');
+ }
+
// set the refresh token
- pluginConfig.token = this.config.token;
+ pluginConfig.credentials.token = login.data.token;
- this.log.warn(`token: ${pluginConfig.token}`);
+ this.debugWarnLog(`token: ${pluginConfig.credentials.token}`);
// save the config, ensuring we maintain pretty json
writeFileSync(this.api.user.configPath(), JSON.stringify(currentConfig, null, 4));
this.verifyConfig();
} catch (e: any) {
- this.log.error(`Update Token: ${e}`);
+ this.errorLog(`Update Token: ${e}`);
}
}
@@ -156,68 +150,65 @@ export class MeaterPlatform implements DynamicPlatformPlugin {
*/
async discoverDevices() {
try {
- if (this.config.token) {
- const { body, statusCode, headers } = await request(meaterUrl, {
+ if (this.config.credentials?.token) {
+ const { body, statusCode } = await request(meaterUrl, {
method: 'GET',
headers: {
- 'Authorization': 'Bearer ' + this.config.token,
+ 'Authorization': 'Bearer ' + this.config.credentials.token,
},
});
- this.log.info(`Device body: ${JSON.stringify(body)}`);
- this.log.info(`Device statusCode: ${statusCode}`);
- this.log.info(`Device headers: ${JSON.stringify(headers)}`);
+ this.debugLog(`Device statusCode: ${statusCode}`);
const device: any = await body.json();
- this.log.info(`Device: ${JSON.stringify(device)}`);
- this.log.info(`Device StatusCode: ${device.statusCode}`);
+ this.debugLog(`Device: ${JSON.stringify(device)}`);
+ this.debugLog(`Device StatusCode: ${device.statusCode}`);
if (statusCode === 200 && device.statusCode === 200) {
- this.log.info (`Found ${device.data.devices.length} Devices`);
+ this.infoLog (`Found ${device.data.devices.length} Devices`);
+ const deviceLists = device.data.devices;
+ await this.configureDevices(deviceLists);
// Meater Devices
- device.data.devices.forEach((device: device & deviceConfig) => {
+ /*device.data.devices.forEach((device: device & deviceConfig) => {
this.createMeter(device);
- });
+ });*/
} else {
this.statusCode(statusCode);
this.statusCode(device.statusCode);
}
} else {
const payload = JSON.stringify({
- email: this.config.email,
- password: this.config.password,
+ email: this.config.credentials?.email,
+ password: this.config.credentials?.password,
});
- const { body, statusCode, headers } = await request(meaterUrlLogin, {
+ const { body, statusCode } = await request(meaterUrlLogin, {
body: payload,
method: 'POST',
headers: { 'content-type': 'application/json' },
});
- this.log.debug(`body: ${JSON.stringify(body)}`);
- this.log.debug(`statusCode: ${statusCode}`);
- this.log.debug(`headers: ${JSON.stringify(headers)}`);
+ this.debugLog(`statusCode: ${statusCode}`);
const login: any = await body.json();
- this.log.debug(`Login: ${JSON.stringify(login)}`);
- this.log.debug(`Login Token: ${JSON.stringify(login.data.token)}`);
- this.log.debug(`Login StatusCode: ${login.statusCode}`);
- this.config.token = login.data.token;
- await this.updateToken();
- this.log.debug(`statusCode: ${statusCode} & devicesAPI StatusCode: ${login.statusCode}`);
+ this.debugLog(`Login: ${JSON.stringify(login)}`);
+ this.debugLog(`Login Token: ${JSON.stringify(login.data.token)}`);
+ this.debugLog(`Login StatusCode: ${login.statusCode}`);
+ await this.updateToken(login);
+ this.debugLog(`statusCode: ${statusCode} & devicesAPI StatusCode: ${login.statusCode}`);
if (statusCode === 200 && login.statusCode === 200) {
- const { body, statusCode, headers } = await request(meaterUrl, {
+ const { body, statusCode } = await request(meaterUrl, {
method: 'GET',
headers: {
Authorization: `Bearer ${login.data.token}}`,
},
});
- this.log.debug(`Device body: ${JSON.stringify(body)}`);
- this.log.debug(`Device statusCode: ${statusCode}`);
- this.log.debug(`Device headers: ${JSON.stringify(headers)}`);
+ this.debugLog(`Device statusCode: ${statusCode}`);
const device: any = await body.json();
- this.log.debug(`Device: ${JSON.stringify(device)}`);
- this.log.debug(`Device StatusCode: ${device.statusCode}`);
+ this.debugLog(`Device: ${JSON.stringify(device)}`);
+ this.debugLog(`Device StatusCode: ${device.statusCode}`);
if (statusCode === 200 && device.statusCode === 200) {
- this.log.info (`Found ${device.data.devices.length} Devices`);
+ this.infoLog (`Found ${device.data.devices.length} Devices`);
+ const deviceLists = device.data.devices;
+ await this.configureDevices(deviceLists);
// Meater Devices
- device.data.devices.forEach((device: device & deviceConfig) => {
+ /*device.data.devices.forEach((device: device & deviceConfig) => {
this.createMeter(device);
- });
+ });*/
} else {
this.statusCode(statusCode);
this.statusCode(device.statusCode);
@@ -225,14 +216,41 @@ export class MeaterPlatform implements DynamicPlatformPlugin {
}
}
} catch (e: any) {
- this.log.error(
+ this.errorLog(
`Failed to Discover Devices, Error Message: ${JSON.stringify(e.message)}, Submit Bugs Here: ` + 'https://bit.ly/homebridge-meater-bug-report',
);
- this.log.error(`Failed to Discover Devices, Error: ${e}`);
+ this.errorLog(`Failed to Discover Devices, Error: ${e}`);
+ }
+ }
+
+ private async configureDevices(deviceLists: any) {
+ if (!this.config.options?.devices) {
+ this.debugLog(`No Meater Device Config: ${JSON.stringify(this.config.options?.devices)}`);
+ const devices = deviceLists.map((v: any) => v);
+ for (const device of devices) {
+ await this.createMeter(device);
+ }
+ } else {
+ this.debugLog(`Meater Device Config Set: ${JSON.stringify(this.config.options?.devices)}`);
+ const deviceConfigs = this.config.options?.devices;
+ if (deviceLists === undefined) {
+ deviceLists = deviceConfigs;
+ }
+
+ const mergeByid = (a1: { id: string; }[], a2: any[]) => a1.map((itm: { id: string; }) => ({
+ ...a2.find((item: { id: string; }) => item.id === itm.id && item),
+ ...itm,
+ }));
+
+ const devices = mergeByid(deviceLists, deviceConfigs);
+ this.debugLog(`Resideo Devices: ${JSON.stringify(devices)}`);
+ for (const device of devices) {
+ await this.createMeter(device);
+ }
}
}
- private async createMeter(device: device & deviceConfig) {
+ private async createMeter(device: device & devicesConfig) {
const uuid = this.api.hap.uuid.generate(device.id);
// see if an accessory with the same uuid has already been registered and restored from
@@ -244,60 +262,76 @@ export class MeaterPlatform implements DynamicPlatformPlugin {
if (await this.registerDevice(device)) {
// if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.:
existingAccessory.context.device.id = device.id;
- this.log.info(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.id}`);
+ existingAccessory.context.displayName = device.configDeviceName || `Meater Thermometer (${device.id.slice(0, 4)})`;
+ existingAccessory.context.FirmwareRevision = await this.FirmwareRevision(device);
+ this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName} DeviceID: ${device.id}`);
this.api.updatePlatformAccessories([existingAccessory]);
// create the accessory handler for the restored accessory
// this is imported from `platformAccessory.ts`
new Meater(this, existingAccessory, device);
- this.log.debug(`uuid: ${device.id}, (${existingAccessory.UUID})`);
+ this.debugLog(`uuid: ${device.id}, (${existingAccessory.UUID})`);
} else {
this.unregisterPlatformAccessories(existingAccessory);
}
} else if (await this.registerDevice(device)) {
// the accessory does not yet exist, so we need to create it
if (!device.external) {
- this.log.info(`Adding new accessory, DeviceID: ${device.id}`);
+ const displayName = device.configDeviceName || `Meater Thermometer (${device.id.slice(0, 4)})`;
+ this.infoLog(`Adding new accessory, Meater: ${displayName}, id: ${device.id}`);
}
// create a new accessory
- const accessory = new this.api.platformAccessory(`Meater Thermometer (${device.id.slice(0, 4)})`, uuid);
+ const accessory = new this.api.platformAccessory((device.configDeviceName || `Meater Thermometer (${device.id.slice(0, 4)})`), uuid);
// store a copy of the device object in the `accessory.context`
// the `context` property can be used to store any data about the accessory you may need
accessory.context.device = device;
accessory.context.device.id = device.id;
+ accessory.context.displayName = device.configDeviceName || `Meater Thermometer (${device.id.slice(0, 4)})`;
+ accessory.context.FirmwareRevision = await this.FirmwareRevision(device);
// create the accessory handler for the newly create accessory
// this is imported from `platformAccessory.ts`
new Meater(this, accessory, device);
- this.log.debug(`uuid: ${device.id}, (${accessory.UUID})`);
+ this.debugLog(`uuid: ${device.id}, (${accessory.UUID})`);
// publish device externally or link the accessory to your platform
this.externalOrPlatform(device, accessory);
this.accessories.push(accessory);
} else {
- this.log.debug(`Device not registered, DeviceID: ${device.id}`);
+ this.debugLog(`Device not registered, DeviceID: ${device.id}`);
}
}
- async registerDevice(device: device & deviceConfig): Promise {
+ async FirmwareRevision(device: device & devicesConfig): Promise {
+ let firmware: any;
+ if (device.firmware) {
+ firmware = device.firmware;
+ } else {
+ firmware = await this.getVersion();
+ }
+ return firmware;
+ }
+
+ async registerDevice(device: device & devicesConfig): Promise {
let registerDevice: boolean;
if (!device.hide_device) {
registerDevice = true;
} else {
registerDevice = false;
- this.log.error(
- `DeviceID: ${device.id} will not display in HomeKit, hide_device: ${device.hide_device}`,
+ const displayName = device.configDeviceName || `Meater Thermometer (${device.id.slice(0, 4)})`;
+ this.errorLog(
+ `Meater: ${displayName}, id: ${device.id} will not display in HomeKit, hide_device: ${device.hide_device}`,
);
}
return registerDevice;
}
- public async externalOrPlatform(device, accessory: PlatformAccessory) {
+ public async externalOrPlatform(device: device & devicesConfig, accessory: PlatformAccessory) {
if (device.external) {
- this.log.warn(`${accessory.displayName} External Accessory Mode`);
+ this.warnLog(`${accessory.displayName} External Accessory Mode`);
this.externalAccessory(accessory);
} else {
- this.log.debug(`${accessory.displayName} External Accessory Mode: ${device.external}`);
+ this.debugLog(`${accessory.displayName} External Accessory Mode: ${device.external}`);
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
}
}
@@ -309,85 +343,133 @@ export class MeaterPlatform implements DynamicPlatformPlugin {
public unregisterPlatformAccessories(existingAccessory: PlatformAccessory) {
// remove platform accessories when no longer present
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]);
- this.log.warn(`Removing existing accessory from cache: ${existingAccessory.displayName}`);
+ this.warnLog(`Removing existing accessory from cache: ${existingAccessory.displayName}`);
}
async statusCode(statusCode: number): Promise {
switch (statusCode) {
case 200:
- this.log.debug(`Standard Response, statusCode: ${statusCode}`);
+ this.debugLog(`Standard Response, statusCode: ${statusCode}`);
break;
case 400:
- this.log.error(`Bad Request, statusCode: ${statusCode}`);
+ this.errorLog(`Bad Request, statusCode: ${statusCode}`);
break;
case 401:
- this.log.error(`Unauthorized, statusCode: ${statusCode}`);
+ this.errorLog(`Unauthorized, statusCode: ${statusCode}`);
break;
case 404:
- this.log.error(`Not Found, statusCode: ${statusCode}`);
+ this.errorLog(`Not Found, statusCode: ${statusCode}`);
break;
case 429:
- this.log.error(`Too Many Requests, statusCode: ${statusCode}`);
+ this.errorLog(`Too Many Requests, statusCode: ${statusCode}`);
break;
case 500:
- this.log.error(`Internal Server Error (Meater Server), statusCode: ${statusCode}`);
+ this.errorLog(`Internal Server Error (Meater Server), statusCode: ${statusCode}`);
break;
default:
- this.log.info(`Unknown statusCode: ${statusCode}, Report Bugs Here: https://bit.ly/homebridge-meater-bug-report`);
+ this.infoLog(`Unknown statusCode: ${statusCode}, Report Bugs Here: https://bit.ly/homebridge-meater-bug-report`);
}
}
- // Utility for debug logging.
- public info(message: string, ...parameters: unknown[]): void {
- if (this.enablingPlatfromLogging()) {
- this.log.info(util.format(message, ...parameters));
+ async platformConfigOptions() {
+ const platformConfig: MeaterPlatformConfig['options'] = {
+ };
+ if (this.config.options?.logging) {
+ platformConfig.logging = this.config.options?.logging;
+ }
+ if (this.config.options?.refreshRate) {
+ platformConfig.refreshRate = this.config.options?.refreshRate;
+ }
+ if (Object.entries(platformConfig).length !== 0) {
+ this.debugLog(`Platform Config: ${JSON.stringify(platformConfig)}`);
+ }
+ this.platformConfig = platformConfig;
+ }
+
+ async platformLogs() {
+ this.debugMode = process.argv.includes('-D') || process.argv.includes('--debug');
+ this.platformLogging = this.config.options?.logging || 'standard';
+ if (this.config.options?.logging === 'debug' || this.config.options?.logging === 'standard' || this.config.options?.logging === 'none') {
+ this.platformLogging = this.config.options.logging;
+ if (this.platformLogging?.includes('debug')) {
+ this.debugWarnLog(`Using Config Logging: ${this.platformLogging}`);
+ }
+ } else if (this.debugMode) {
+ this.platformLogging = 'debugMode';
+ if (this.platformLogging?.includes('debug')) {
+ this.debugWarnLog(`Using ${this.platformLogging} Logging`);
+ }
+ } else {
+ this.platformLogging = 'standard';
+ if (this.platformLogging?.includes('debug')) {
+ this.debugWarnLog(`Using ${this.platformLogging} Logging`);
+ }
}
+ if (this.debugMode) {
+ this.platformLogging = 'debugMode';
+ }
+ }
+
+ async getVersion() {
+ const json = JSON.parse(
+ readFileSync(
+ new URL('../package.json', import.meta.url),
+ 'utf-8',
+ ),
+ );
+ this.debugLog(`Plugin Version: ${json.version}`);
+ return json.version;
}
- // Utility for warn logging.
- public warn(message: string, ...parameters: unknown[]): void {
+ /**
+ * If device level logging is turned on, log to log.warn
+ * Otherwise send debug logs to log.debug
+ */
+ infoLog(...log: any[]): void {
if (this.enablingPlatfromLogging()) {
- this.log.info(util.format(message, ...parameters));
+ this.log.info(String(...log));
}
}
- // Utility for debug logging.
- public error(message: string, ...parameters: unknown[]): void {
+ warnLog(...log: any[]): void {
if (this.enablingPlatfromLogging()) {
- this.log.error(util.format(message, ...parameters));
+ this.log.warn(String(...log));
}
}
- // Utility for debug logging.
- public debug(message: string, ...parameters: unknown[]): void {
+ debugWarnLog(...log: any[]): void {
if (this.enablingPlatfromLogging()) {
- if (this.config.logging === 'debugMode') {
- this.log.debug(util.format(message, ...parameters));
- } else if (this.config.logging === 'debug') {
- this.log.info(util.format(message, ...parameters));
+ if (this.platformLogging?.includes('debug')) {
+ this.log.warn('[DEBUG]', String(...log));
}
}
}
- // Utility for debug logging.
- public debugError(message: string, ...parameters: unknown[]): void {
+ errorLog(...log: any[]): void {
if (this.enablingPlatfromLogging()) {
- if (this.config.logging === 'debugMode') {
- this.log.debug(util.format(message, ...parameters));
- } else if (this.config.logging === 'debug') {
- this.log.info(util.format(message, ...parameters));
+ this.log.error(String(...log));
+ }
+ }
+
+ debugErrorLog(...log: any[]): void {
+ if (this.enablingPlatfromLogging()) {
+ if (this.platformLogging?.includes('debug')) {
+ this.log.error('[DEBUG]', String(...log));
}
}
}
- // Utility for debug logging.
- public debugWarn(message: string, ...parameters: unknown[]): void {
+ debugLog(...log: any[]): void {
if (this.enablingPlatfromLogging()) {
- this.log.info(util.format(message, ...parameters));
+ if (this.platformLogging === 'debugMode') {
+ this.log.debug(String(...log));
+ } else if (this.platformLogging === 'debug') {
+ this.log.info('[DEBUG]', String(...log));
+ }
}
}
- private enablingPlatfromLogging() {
- return this.config.logging?.includes('debug') || this.config.logging === 'standard';
+ enablingPlatfromLogging(): boolean {
+ return this.platformLogging?.includes('debug') || this.platformLogging === 'standard';
}
}
diff --git a/src/settings.ts b/src/settings.ts
index 2927344..9422457 100644
--- a/src/settings.ts
+++ b/src/settings.ts
@@ -1,3 +1,7 @@
+/* Copyright(C) 2023-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
+ *
+ * setting.ts: homebridge-meater.
+ */
/* eslint-disable max-len */
import { PlatformConfig } from 'homebridge';
/**
@@ -21,16 +25,30 @@ export const meaterUrlLogin = 'https://public-api.cloud.meater.com/v1/login';
//Config
export interface MeaterPlatformConfig extends PlatformConfig {
+ credentials?: credentials;
+ options?: options | Record;
+}
+
+export type credentials = {
email?: string;
password?: string;
token?: string;
- logging?: string;
+};
+
+export type options = {
+ devices?: Array;
refreshRate?: number;
-}
+ logging?: string;
+};
-export type deviceConfig = {
- hide_device: boolean;
- external: boolean;
+export interface devicesConfig extends device {
+ id: string;
+ configDeviceName?: string;
+ hide_device?: boolean;
+ firmware?: string;
+ external?: boolean;
+ refreshRate?: number;
+ logging?: string;
}
export type getDevice = {
@@ -49,6 +67,11 @@ export type device = {
temperature: Temperature;
cook: Cook;
updated_at: number;
+ data: deviceData;
+}
+
+export type deviceData = {
+ temperature: Temperature;
}
export type Temperature = {