diff --git a/config.schema.json b/config.schema.json index 0727d39..fe5548d 100644 --- a/config.schema.json +++ b/config.schema.json @@ -22,6 +22,36 @@ "description": "Default: ~/.homebridge/.yamahaAVR", "type": "string", "required": false + }, + "enablePureDirectSwitch": { + "title": "Enable Pure Direct Switch Device", + "type": "boolean", + "default": false, + "required": false + }, + "volumeAccessoryEnabled": { + "title": "Enable Volume Devices", + "type": "boolean", + "default": false, + "required": false + }, + "zone2Enabled": { + "title": "Enable Zone 2", + "type": "boolean", + "default": false, + "required": false + }, + "zone3Enabled": { + "title": "Enable Zone 3", + "type": "boolean", + "default": false, + "required": false + }, + "zone4Enabled": { + "title": "Enable Zone 4", + "type": "boolean", + "default": false, + "required": false } } } diff --git a/package-lock.json b/package-lock.json index 7bbd631..9685477 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,19 @@ { "name": "homebridge-yamaha-avr", - "version": "2.1.1", + "version": "3.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "homebridge-yamaha-avr", - "version": "2.1.1", + "version": "3.0.0", "license": "UNLICENSED", "dependencies": { "fs-extra": "^10.1.0", - "node-fetch": "^2.6.7", - "yamaha-nodejs": "^0.9.6" + "node-fetch": "^3.2.10" }, "devDependencies": { + "@types/fs-extra": "^9.0.13", "@types/node": "^18.7.13", "@types/node-persist": "^3.1.0", "@typescript-eslint/eslint-plugin": "^5.35.1", @@ -30,7 +30,7 @@ }, "engines": { "homebridge": ">=1.3.5", - "node": ">=14.18.1" + "node": ">=14.15.4" } }, "node_modules/@babel/code-frame": { @@ -302,6 +302,15 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -547,6 +556,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -634,22 +644,6 @@ "node": ">=8" } }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "engines": { - "node": ">=0.8" - } - }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -659,11 +653,6 @@ "node": ">=8" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -676,38 +665,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/bcrypt-pbkdf/node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" - }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -717,11 +680,6 @@ "node": ">=8" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, "node_modules/bonjour-hap": { "version": "3.6.3", "resolved": "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.6.3.tgz", @@ -785,11 +743,6 @@ "node": ">=6" } }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -851,17 +804,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", @@ -877,11 +819,6 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" - }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -902,15 +839,12 @@ "node": ">= 8" } }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dependencies": { - "assert-plus": "^1.0.0" - }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", "engines": { - "node": ">=0.10" + "node": ">= 12" } }, "node_modules/debug": { @@ -978,14 +912,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -1031,15 +957,6 @@ "node": ">=6.0.0" } }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1432,23 +1349,11 @@ "node": ">=0.10.0" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "engines": [ - "node >=0.6.0" - ] - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "node_modules/fast-diff": { "version": "1.2.0", @@ -1475,7 +1380,8 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -1501,6 +1407,28 @@ "reusify": "^1.0.4" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -1553,25 +1481,15 @@ "is-callable": "^1.1.3" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "fetch-blob": "^3.1.2" }, "engines": { - "node": ">= 0.12" + "node": ">=12.20.0" } }, "node_modules/fs-extra": { @@ -1685,14 +1603,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dependencies": { - "assert-plus": "^1.0.0" - } - }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1786,27 +1696,6 @@ "node": ">=10.17.0" } }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -1897,20 +1786,6 @@ "node": ">=10.17.0" } }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -2231,11 +2106,6 @@ "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", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, "node_modules/is-weakmap": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", @@ -2282,11 +2152,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2306,20 +2171,11 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -2327,11 +2183,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -2343,20 +2194,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2422,25 +2259,6 @@ "node": ">=8.6" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2502,23 +2320,39 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", + "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", "dependencies": { - "whatwg-url": "^5.0.0" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" }, "engines": { - "node": "4.x || >=6.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, "node_modules/node-persist": { @@ -2623,14 +2457,6 @@ "node": ">=0.10.0" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "engines": { - "node": "*" - } - }, "node_modules/object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -2748,16 +2574,6 @@ "node": ">=8" } }, - "node_modules/peer-ssdp": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/peer-ssdp/-/peer-ssdp-0.0.5.tgz", - "integrity": "sha512-ckMRz+ZaaX54i/r4ZP/JyBxaGs/bxT4Q4I7sDHqcRGazfHCurXRh22PxNaJr+W6s9cQjfa5fKXqU4BO3YHiszw==" - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -2815,11 +2631,6 @@ "node": ">=0.4.0" } }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -2830,6 +2641,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, "engines": { "node": ">=6" } @@ -2853,14 +2665,6 @@ "qrcode-terminal": "bin/qrcode-terminal.js" } }, - "node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "engines": { - "node": ">=0.6" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -2922,37 +2726,6 @@ "url": "https://github.com/sponsors/mysticatea" } }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -3019,35 +2792,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, "node_modules/semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -3170,35 +2914,6 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "node_modules/sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sshpk/node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -3351,23 +3066,6 @@ "nodetouch": "bin/nodetouch.js" } }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "node_modules/ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -3438,17 +3136,6 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", @@ -3525,19 +3212,11 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "dependencies": { "punycode": "^2.1.0" } }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -3550,31 +3229,12 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "engines": { + "node": ">= 8" } }, "node_modules/which": { @@ -3658,57 +3318,12 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "node_modules/xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "engines": { - "node": ">=4.0" - } - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/yamaha-nodejs": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/yamaha-nodejs/-/yamaha-nodejs-0.9.6.tgz", - "integrity": "sha512-sn/X2d8X3QhlhDvwhwXG/N65zIwc/stNBcknXG2YdVd81wE1fUfRMVzk317jhAs5Kn3uWZt0s7zeUKCVo0TcJg==", - "dependencies": { - "bluebird": "^3.3.4", - "debug": "^2.2.0", - "peer-ssdp": "0.0.5", - "request": "^2.88.0", - "xml2js": "^0.4.16" - } - }, - "node_modules/yamaha-nodejs/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/yamaha-nodejs/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -3945,6 +3560,15 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "dev": true }, + "@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -4090,6 +3714,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4155,78 +3780,30 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" - }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, "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==", "dev": true }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" - }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "requires": { - "tweetnacl": "^0.14.3" - }, - "dependencies": { - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" - } - } - }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, "bonjour-hap": { "version": "3.6.3", "resolved": "https://registry.npmjs.org/bonjour-hap/-/bonjour-hap-3.6.3.tgz", @@ -4281,11 +3858,6 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" - }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4327,14 +3899,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, "commander": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", @@ -4347,11 +3911,6 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" - }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -4369,13 +3928,10 @@ "which": "^2.0.1" } }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "requires": { - "assert-plus": "^1.0.0" - } + "data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==" }, "debug": { "version": "4.3.4", @@ -4425,11 +3981,6 @@ "object-keys": "^1.1.1" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, "diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -4463,15 +4014,6 @@ "esutils": "^2.0.2" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -4756,20 +4298,11 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, "fast-diff": { "version": "1.2.0", @@ -4793,7 +4326,8 @@ "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, "fast-levenshtein": { "version": "2.0.6", @@ -4816,6 +4350,15 @@ "reusify": "^1.0.4" } }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -4859,19 +4402,12 @@ "is-callable": "^1.1.3" } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "fetch-blob": "^3.1.2" } }, "fs-extra": { @@ -4954,14 +4490,6 @@ "get-intrinsic": "^1.1.1" } }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "requires": { - "assert-plus": "^1.0.0" - } - }, "glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -5031,20 +4559,6 @@ "tweetnacl": "^1.0.3" } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -5105,16 +4619,6 @@ "source-map-support": "^0.5.20" } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -5333,11 +4837,6 @@ "has-tostringtag": "^1.0.0" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, "is-weakmap": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", @@ -5375,11 +4874,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5396,20 +4890,11 @@ "esprima": "^4.0.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" - }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -5417,11 +4902,6 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, "jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -5431,17 +4911,6 @@ "universalify": "^2.0.0" } }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -5495,19 +4964,6 @@ "picomatch": "^2.3.1" } }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5560,12 +5016,19 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" + }, "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", + "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", "requires": { - "whatwg-url": "^5.0.0" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" } }, "node-persist": { @@ -5643,11 +5106,6 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, "object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -5732,16 +5190,6 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, - "peer-ssdp": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/peer-ssdp/-/peer-ssdp-0.0.5.tgz", - "integrity": "sha512-ckMRz+ZaaX54i/r4ZP/JyBxaGs/bxT4Q4I7sDHqcRGazfHCurXRh22PxNaJr+W6s9cQjfa5fKXqU4BO3YHiszw==" - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -5775,11 +5223,6 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, - "psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, "pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -5789,7 +5232,8 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true }, "q": { "version": "1.1.2", @@ -5803,11 +5247,6 @@ "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", "dev": true }, - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" - }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5840,33 +5279,6 @@ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", "dev": true }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, "require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -5903,21 +5315,6 @@ "queue-microtask": "^1.2.2" } }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, "semver": { "version": "7.3.7", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", @@ -6009,29 +5406,6 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "dependencies": { - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" - } - } - }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -6152,20 +5526,6 @@ "nopt": "~1.0.10" } }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "ts-node": { "version": "10.9.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", @@ -6210,14 +5570,6 @@ } } }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, "tweetnacl": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", @@ -6272,15 +5624,11 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, "requires": { "punycode": "^2.1.0" } }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -6293,29 +5641,10 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } + "web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==" }, "which": { "version": "2.0.2", @@ -6377,53 +5706,12 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" - }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "yamaha-nodejs": { - "version": "0.9.6", - "resolved": "https://registry.npmjs.org/yamaha-nodejs/-/yamaha-nodejs-0.9.6.tgz", - "integrity": "sha512-sn/X2d8X3QhlhDvwhwXG/N65zIwc/stNBcknXG2YdVd81wE1fUfRMVzk317jhAs5Kn3uWZt0s7zeUKCVo0TcJg==", - "requires": { - "bluebird": "^3.3.4", - "debug": "^2.2.0", - "peer-ssdp": "0.0.5", - "request": "^2.88.0", - "xml2js": "^0.4.16" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, "yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 7e1738c..06b00bd 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "license": "UNLICENSED", "displayName": "Yamaha AVR", "name": "homebridge-yamaha-avr", - "version": "2.1.1", + "version": "3.0.0", "description": "homebridge-plugin - Add a Yamaha AVR as a HomeKit Audio Receiver with Power, Input, Volume & Remote Control", "author": { "name": "ACDR", @@ -17,15 +17,15 @@ "url": "https://github.com/ACDR/homebridge-yamaha-avr/issues" }, "engines": { - "node": ">=14.18.1", + "node": ">=14.15.4", "homebridge": ">=1.3.5" }, "dependencies": { "fs-extra": "^10.1.0", - "node-fetch": "^2.6.7", - "yamaha-nodejs": "^0.9.6" + "node-fetch": "^3.2.10" }, "devDependencies": { + "@types/fs-extra": "^9.0.13", "@types/node": "^18.7.13", "@types/node-persist": "^3.1.0", "@typescript-eslint/eslint-plugin": "^5.35.1", @@ -40,7 +40,9 @@ "ts-node": "^10.9.1", "typescript": "4.7.4" }, + "type": "module", "main": "dist/index.js", + "exports": "./dist/index.js", "scripts": { "watch": "npm run build && npm link && nodemon", "build": "rimraf ./dist && tsc", diff --git a/src/accessory.ts b/src/accessory.ts index dbeb789..f9fc8e7 100644 --- a/src/accessory.ts +++ b/src/accessory.ts @@ -1,19 +1,20 @@ +import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import fetch, { Response } from 'node-fetch'; + +import { YamahaAVRPlatform } from './platform.js'; +import { StorageService } from './storageService.js'; import { - Service, - PlatformAccessory, - CharacteristicValue, - CharacteristicSetCallback, - CharacteristicGetCallback, -} from 'homebridge'; -import fetch from 'node-fetch'; -import { YamahaAVRPlatform } from './platform'; -import { PLUGIN_NAME } from './settings'; -import { StorageService } from './storageService'; - -interface Input { - id: string; - name: string; -} + AccessoryContext, + BaseResponse, + Cursor, + Features, + Input, + MainZoneRemoteCode, + NameText, + Zone, + ZoneStatus, +} from './types.js'; +import { getZoneStatus } from './utils/getZoneStatus.js'; interface CachedServiceData { Identifier: number; @@ -21,53 +22,51 @@ interface CachedServiceData { ConfiguredName: string; } -export interface AccessoryContext { - systemId?: string; - modelName?: string; - firmwareVersion?: string; - device: { - UUID: string; - displayName: string; - }; - features: string[]; -} - export class YamahaAVRAccessory { + private baseApiUrl: AccessoryContext['device']['baseApiUrl']; + private cacheDirectory: string; private service: Service; private inputServices: Service[] = []; private storageService: StorageService; - private state = { - isPlaying: true as boolean, - inputs: [] as Input[], - connectionError: false as boolean, + private state: { + isPlaying: boolean; // TODO: Investigaste a better way of tracking "playing" state + inputs: Input[]; + connectionError: boolean; + } = { + isPlaying: true, + inputs: [], + connectionError: false, }; constructor( private readonly platform: YamahaAVRPlatform, private readonly accessory: PlatformAccessory, + private readonly zone: Zone['id'], ) { - const cacheDirectory = this.platform.config.cacheDirectory || this.platform.api.user.storagePath() + '/.yamahaAVR/'; - this.storageService = new StorageService(cacheDirectory); + this.cacheDirectory = + this.platform.config.cacheDirectory || this.platform.api.user.storagePath() + '/.yamahaAVR/' + this.zone; + this.storageService = new StorageService(this.cacheDirectory); this.storageService.initSync(); // set the AVR accessory information this.accessory .getService(this.platform.Service.AccessoryInformation)! .setCharacteristic(this.platform.Characteristic.Manufacturer, 'Yamaha') - .setCharacteristic(this.platform.Characteristic.Model, this.accessory.context.modelName || 'Unknown') - .setCharacteristic(this.platform.Characteristic.SerialNumber, this.accessory.context.systemId || 'Unknown') + .setCharacteristic(this.platform.Characteristic.Model, this.accessory.context.device.modelName) + .setCharacteristic(this.platform.Characteristic.SerialNumber, this.accessory.context.device.systemId) .setCharacteristic( this.platform.Characteristic.FirmwareRevision, - this.accessory.context.firmwareVersion || 'Unknown', + `${this.accessory.context.device.firmwareVersion}`, ); this.service = this.accessory.addService(this.platform.Service.Television); + this.baseApiUrl = this.accessory.context.device.baseApiUrl; this.init(); // regularly ping the AVR to keep power/input state syncronised - setInterval(this.getPowerState.bind(this, this.updateAVRState.bind(this)), 5000); + setInterval(this.updateAVRState.bind(this), 5000); } async init() { @@ -75,9 +74,6 @@ export class YamahaAVRAccessory { await this.createTVService(); await this.createTVSpeakerService(); await this.createInputSourceServices(); - - // Wait for all services to be created before publishing - this.platform.api.publishExternalAccessories(PLUGIN_NAME, [this.accessory]); } catch (err) { this.platform.log.error(err as string); } @@ -95,109 +91,17 @@ export class YamahaAVRAccessory { // Power State Get/Set this.service .getCharacteristic(this.platform.Characteristic.Active) - .on('get', this.getPowerState.bind(this)) - .on('set', this.setPowerState.bind(this)); + .onSet(this.setPowerState.bind(this)) + .onGet(this.getPowerState.bind(this)); // Input Source Get/Set this.service .getCharacteristic(this.platform.Characteristic.ActiveIdentifier) - .on('get', this.getInputState.bind(this)) - .on('set', this.setInputState.bind(this)); + .onSet(this.setInputState.bind(this)) + .onGet(this.getInputState.bind(this)); // Remote Key Set - this.service.getCharacteristic(this.platform.Characteristic.RemoteKey).on('set', (newValue, callback) => { - const sendRemoteCode = (remoteKey, callback) => { - fetch(`http://${this.platform.config.ip}/YamahaExtendedControl/v1/system/sendIrCode?code=${remoteKey}`).then( - (response) => { - callback(response); - }, - ); - }; - - switch (newValue) { - case this.platform.Characteristic.RemoteKey.REWIND: - this.platform.log.info('set Remote Key Pressed: REWIND'); - this.platform.YamahaAVR.rewind(); - callback(null); - break; - - case this.platform.Characteristic.RemoteKey.FAST_FORWARD: - this.platform.log.info('set Remote Key Pressed: FAST_FORWARD'); - this.platform.YamahaAVR.skip(); - callback(null); - break; - - case this.platform.Characteristic.RemoteKey.NEXT_TRACK: - this.platform.log.info('set Remote Key Pressed: NEXT_TRACK'); - sendRemoteCode('7F016D92', callback); - break; - - case this.platform.Characteristic.RemoteKey.PREVIOUS_TRACK: - this.platform.log.info('set Remote Key Pressed: PREVIOUS_TRACK'); - sendRemoteCode('7F016C93', callback); - break; - - case this.platform.Characteristic.RemoteKey.ARROW_UP: - this.platform.log.info('set Remote Key Pressed: ARROW_UP'); - sendRemoteCode('7A859D62', callback); - break; - - case this.platform.Characteristic.RemoteKey.ARROW_DOWN: - this.platform.log.info('set Remote Key Pressed: ARROW_DOWN'); - sendRemoteCode('7A859C63', callback); - break; - - case this.platform.Characteristic.RemoteKey.ARROW_LEFT: - this.platform.log.info('set Remote Key Pressed: ARROW_LEFT'); - sendRemoteCode('7A859F60', callback); - break; - - case this.platform.Characteristic.RemoteKey.ARROW_RIGHT: - this.platform.log.info('set Remote Key Pressed: ARROW_RIGHT'); - sendRemoteCode('7A859E61', callback); - break; - - case this.platform.Characteristic.RemoteKey.SELECT: - this.platform.log.info('set Remote Key Pressed: SELECT'); - sendRemoteCode('7A85DE21', callback); - break; - - case this.platform.Characteristic.RemoteKey.BACK: - this.platform.log.info('set Remote Key Pressed: BACK'); - sendRemoteCode('7A85AA55', callback); - break; - - case this.platform.Characteristic.RemoteKey.EXIT: - this.platform.log.info('set Remote Key Pressed: EXIT'); - sendRemoteCode('7A85AA55', callback); - break; - - case this.platform.Characteristic.RemoteKey.PLAY_PAUSE: - this.platform.log.info('set Remote Key Pressed: PLAY_PAUSE'); - if (this.state.isPlaying) { - this.platform.YamahaAVR.pause(); - // this.sendRemoteCode('7F016798', callback); - } else { - this.platform.YamahaAVR.play(); - // this.sendRemoteCode('7F016897', callback); - } - - this.state.isPlaying = !this.state.isPlaying; - - callback(null); - - break; - - case this.platform.Characteristic.RemoteKey.INFORMATION: - this.platform.log.info('set Remote Key Pressed: INFORMATION'); - sendRemoteCode('7A851F60', callback); - break; - - default: - this.platform.log.info('unhandled Remote Key Pressed'); - break; - } - }); + this.service.getCharacteristic(this.platform.Characteristic.RemoteKey).onSet(this.setRemoteKey.bind(this)); return; } @@ -213,11 +117,7 @@ export class YamahaAVRAccessory { ); // handle volume control - speakerService - .getCharacteristic(this.platform.Characteristic.VolumeSelector) - .on('set', (direction: CharacteristicValue, callback: CharacteristicSetCallback) => { - this.setVolume(direction, callback); - }); + speakerService.getCharacteristic(this.platform.Characteristic.VolumeSelector).onSet(this.setVolume.bind(this)); return; } @@ -233,13 +133,13 @@ export class YamahaAVRAccessory { const inputService = this.accessory.addService( this.platform.Service.InputSource, this.platform.api.hap.uuid.generate(input.id), - input.name, + input.text, ); inputService .setCharacteristic(this.platform.Characteristic.Identifier, i) - .setCharacteristic(this.platform.Characteristic.Name, input.name) - .setCharacteristic(this.platform.Characteristic.ConfiguredName, cachedService?.ConfiguredName || input.name) + .setCharacteristic(this.platform.Characteristic.Name, input.text) + .setCharacteristic(this.platform.Characteristic.ConfiguredName, cachedService?.ConfiguredName || input.text) .setCharacteristic( this.platform.Characteristic.IsConfigured, this.platform.Characteristic.IsConfigured.CONFIGURED, @@ -258,33 +158,45 @@ export class YamahaAVRAccessory { ); // Update input name cache - inputService.getCharacteristic(this.platform.Characteristic.ConfiguredName).on('set', (name, callback) => { - this.platform.log.debug(`Set input (${input.id}) name to ${name}`); + inputService + .getCharacteristic(this.platform.Characteristic.ConfiguredName) + .onGet((): CharacteristicValue => cachedService?.ConfiguredName || input.text) + .onSet((name: CharacteristicValue) => { + const currentConfiguredName = inputService.getCharacteristic( + this.platform.Characteristic.ConfiguredName, + ).value; - let configuredName = name; + if (name === currentConfiguredName) { + return; + } - if (!name || input.name === name) { - this.platform.log.debug(`Custom name not provided, clearing configured input name for`, input.name); + this.platform.log.debug(`Set input (${input.id}) name to ${name} `); - configuredName = input.name; - } + const configuredName = name || input.text; - inputService.updateCharacteristic(this.platform.Characteristic.ConfiguredName, configuredName); + inputService.updateCharacteristic(this.platform.Characteristic.ConfiguredName, configuredName); - this.storageService.setItemSync(input.id, { - ConfiguredName: configuredName, - CurrentVisibilityState: inputService.getCharacteristic( - this.platform.Characteristic.CurrentVisibilityState, - ).value, + this.storageService.setItemSync(input.id, { + ConfiguredName: configuredName, + CurrentVisibilityState: inputService.getCharacteristic( + this.platform.Characteristic.CurrentVisibilityState, + ).value, + }); }); - callback(null); - }); - // Update input visibility cache inputService .getCharacteristic(this.platform.Characteristic.TargetVisibilityState) - .on('set', (targetVisibilityState, callback) => { + .onGet((): CharacteristicValue => cachedService?.CurrentVisibilityState || 0) + .onSet((targetVisibilityState: CharacteristicValue) => { + const currentVisbility = inputService.getCharacteristic( + this.platform.Characteristic.CurrentVisibilityState, + ).value; + + if (targetVisibilityState === currentVisbility) { + return; + } + const isHidden = targetVisibilityState === this.platform.Characteristic.TargetVisibilityState.HIDDEN; this.platform.log.debug(`Set input (${input.id}) visibility state to ${isHidden ? 'HIDDEN' : 'SHOWN'} `); @@ -296,27 +208,26 @@ export class YamahaAVRAccessory { this.storageService.setItemSync(input.id, { ConfiguredName: - inputService.getCharacteristic(this.platform.Characteristic.ConfiguredName).value || input.name, + inputService.getCharacteristic(this.platform.Characteristic.ConfiguredName).value || input.text, CurrentVisibilityState: targetVisibilityState, }); - - callback(null); }); inputService .getCharacteristic(this.platform.Characteristic.Name) - .on('get', (callback) => callback(null, input.name)); + .onGet((): CharacteristicValue => input.text); if (cachedService) { if (this.platform.Characteristic.CurrentVisibilityState.SHOWN !== cachedService.CurrentVisibilityState) { this.platform.log.debug(`Restoring input ${input.id} visibility state from cache`); + inputService.setCharacteristic( this.platform.Characteristic.CurrentVisibilityState, cachedService.CurrentVisibilityState, ); } - if (input.name !== cachedService.ConfiguredName && cachedService.ConfiguredName !== '') { + if (input.text !== cachedService.ConfiguredName && cachedService.ConfiguredName !== '') { this.platform.log.debug(`Restoring input ${input.id} configured name from cache`); inputService.setCharacteristic(this.platform.Characteristic.ConfiguredName, cachedService.ConfiguredName); } @@ -328,7 +239,7 @@ export class YamahaAVRAccessory { try { // Cache Data const name = - inputService.getCharacteristic(this.platform.Characteristic.ConfiguredName).value || input.name; + inputService.getCharacteristic(this.platform.Characteristic.ConfiguredName).value || input.text; const visibility = inputService.getCharacteristic( this.platform.Characteristic.CurrentVisibilityState, ).value; @@ -351,16 +262,18 @@ export class YamahaAVRAccessory { resolve(); } } catch (err) { - reject(` + reject( + ` Could not write to cache. Please check your Homebridge instance has permission to write to - "${this.platform.config.cacheDirectory}" + "${this.cacheDirectory}" or set a different cache directory using the "cacheDirectory" config property. - `); + `, + ); } } catch (err) { this.platform.log.error(` - Failed to add input service ${input.name}: + Failed to add input service ${input.id}: ${err} `); } @@ -369,144 +282,259 @@ export class YamahaAVRAccessory { } async updateInputSources() { - const features = this.accessory.context.features; + try { + const featuresResponse = await fetch(`${this.baseApiUrl}/system/getFeatures`); + const features = (await featuresResponse.json()) as Features; + const zoneInputs = features.zone.find((zone) => zone.id === this.zone)?.input_list; - return this.platform.YamahaAVR.getAvailableInputsWithNames() - .then((availableInputs) => { - this.platform.log.debug('features', features); - this.platform.log.debug('availableInputs', availableInputs); + if (!zoneInputs) { + throw new Error(); + } - const inputs = [ - ...features, - ...Object.keys(availableInputs[0]).filter( - (input) => !features.map((feature) => feature.toUpperCase()).includes(input.toUpperCase()), - ), - ].map((input) => ({ - id: input.replace('_', ''), - name: (availableInputs[0][input] ? availableInputs[0][input][0] : input).replace('_', ' '), - })); + const getNameTextResponse = await fetch(`${this.baseApiUrl}/system/getNameText`); + const nameText = (await getNameTextResponse.json()) as NameText; - this.platform.log.debug('inputs', inputs); + this.state.inputs = nameText.input_list.filter((input) => zoneInputs.includes(input.id)); + } catch { + this.platform.log.error(` + Failed to get available inputs from ${this.platform.config.name}. + Please verify the AVR is connected and accessible at ${this.platform.config.ip} + `); + } + } - this.state.inputs = inputs; + async updateAVRState() { + try { + const zoneStatus = await getZoneStatus(this.platform, this.accessory, this.zone); - return; - }) - .catch(() => { - this.platform.log.error(` - Failed to get available inputs from ${this.platform.config.name}. - Please verify the AVR is connected and accessible at ${this.platform.config.ip} - `); + if (!zoneStatus) { + throw new Error(); + } + + this.platform.log.debug(`AVR PING`, { power: zoneStatus.power, input: zoneStatus.input }); + this.service.updateCharacteristic(this.platform.Characteristic.Active, zoneStatus.power === 'on'); + + this.service.updateCharacteristic( + this.platform.Characteristic.ActiveIdentifier, + this.state.inputs.findIndex((input) => input.id === zoneStatus.input), + ); + + if (this.state.connectionError) { + this.state.connectionError = false; + this.platform.log.info(`Communication with Yamaha AVR at ${this.platform.config.ip} restored`); + } + } catch (error) { + if (this.state.connectionError) { return; - }); + } + + this.state.connectionError = true; + this.platform.log.error(` + Cannot communicate with Yamaha AVR at ${this.platform.config.ip}. + Connection will be restored automatically when the AVR begins responding. + `); + } } - updateAVRState(error, status) { - this.service.updateCharacteristic(this.platform.Characteristic.Active, status); + async getPowerState(): Promise { + const zoneStatus = await getZoneStatus(this.platform, this.accessory, this.zone); - this.platform.YamahaAVR.getBasicInfo() - .then(async (basicInfo) => { - await this.updateInputSources(); + if (!zoneStatus) { + return false; + } - const currentInputIndex = this.state.inputs.findIndex( - (input) => input.id === basicInfo.getCurrentInput().replace(/[^a-z0-9]/gi, ''), - ); + return zoneStatus.power === 'on'; + } - if (currentInputIndex > -1) { - this.service.updateCharacteristic(this.platform.Characteristic.ActiveIdentifier, currentInputIndex); - } else { - this.platform.log.error(`Unexpected input: "${basicInfo.getCurrentInput()}"`, this.state.inputs); - } + async setPowerState(state: CharacteristicValue) { + try { + let setPowerResponse: Response; + + if (state) { + setPowerResponse = await fetch(`${this.baseApiUrl}/${this.zone}/setPower?power=on`); + } else { + setPowerResponse = await fetch(`${this.baseApiUrl}/${this.zone}/setPower?power=standby`); + } + + const responseJson = (await setPowerResponse.json()) as BaseResponse; + + if (responseJson.response_code !== 0) { + throw new Error('Failed to set zone power'); + } + } catch (error) { + this.platform.log.error((error as Error).message); + } + } + + async setRemoteKey(remoteKey: CharacteristicValue) { + try { + const sendRemoteCode = async (remoteKey: MainZoneRemoteCode) => { + const sendIrCodeResponse = await fetch(`${this.baseApiUrl}/system/sendIrCode?code=${remoteKey}`); + const responseJson = (await sendIrCodeResponse.json()) as BaseResponse; - if (this.state.connectionError) { - this.state.connectionError = false; - this.platform.log.info(`Communication with Yamaha AVR at ${this.platform.config.ip} restored`); + if (responseJson.response_code !== 0) { + throw new Error('Failed to send ir code'); } + }; - return; - }) - .catch(() => { - if (this.state.connectionError) { - return; + const controlCursor = async (cursor: Cursor) => { + const controlCursorResponse = await fetch(`${this.baseApiUrl}/${this.zone}/controlCursor?cursor=${cursor}`); + const responseJson = (await controlCursorResponse.json()) as BaseResponse; + if (responseJson.response_code !== 0) { + throw new Error('Failed to control cursor'); } + }; - this.state.connectionError = true; - this.platform.log.error(` - Cannot communicate with Yamaha AVR at ${this.platform.config.ip}. - Connection will be restored automatically when the AVR begins responding. - `); - }); - } + switch (remoteKey) { + case this.platform.Characteristic.RemoteKey.REWIND: + this.platform.log.info('set Remote Key Pressed: REWIND'); + sendRemoteCode(MainZoneRemoteCode.SEARCH_BACK); + break; - getPowerState(callback: CharacteristicGetCallback) { - this.platform.YamahaAVR.isOn() - .then((result) => { - callback(null, result); - }) - .catch((error) => { - callback(error, false); - }); - } + case this.platform.Characteristic.RemoteKey.FAST_FORWARD: + this.platform.log.info('set Remote Key Pressed: FAST_FORWARD'); + sendRemoteCode(MainZoneRemoteCode.SEARCH_FWD); + break; - setPowerState(state: CharacteristicValue, callback: CharacteristicSetCallback) { - if (state) { - this.platform.log.info('Power On'); - this.platform.YamahaAVR.powerOn(); - } else { - this.platform.log.info('Power Off'); - this.platform.YamahaAVR.powerOff(); - } + case this.platform.Characteristic.RemoteKey.NEXT_TRACK: + this.platform.log.info('set Remote Key Pressed: NEXT_TRACK'); + sendRemoteCode(MainZoneRemoteCode.SKIP_FWD); + break; - callback(null); - } + case this.platform.Characteristic.RemoteKey.PREVIOUS_TRACK: + this.platform.log.info('set Remote Key Pressed: PREVIOUS_TRACK'); + sendRemoteCode(MainZoneRemoteCode.SKIP_BACK); + break; - setVolume(direction: CharacteristicValue, callback: CharacteristicSetCallback) { - this.platform.YamahaAVR.getBasicInfo() - .then((basicInfo) => { - const volume = basicInfo.getVolume(); - - if (direction === 0) { - this.platform.log.info('Volume Up', (volume + 5) / 10); - this.platform.YamahaAVR.volumeUp(5); - } else { - this.platform.log.info('Volume Down', (volume - 5) / 10); - this.platform.YamahaAVR.volumeDown(5); - } + case this.platform.Characteristic.RemoteKey.ARROW_UP: + this.platform.log.info('set Remote Key Pressed: ARROW_UP'); + controlCursor('up'); + break; - callback(null); - }) - .catch((error) => { - callback(error, false); - }); + case this.platform.Characteristic.RemoteKey.ARROW_DOWN: + this.platform.log.info('set Remote Key Pressed: ARROW_DOWN'); + controlCursor('down'); + break; + + case this.platform.Characteristic.RemoteKey.ARROW_LEFT: + this.platform.log.info('set Remote Key Pressed: ARROW_LEFT'); + controlCursor('left'); + break; + + case this.platform.Characteristic.RemoteKey.ARROW_RIGHT: + this.platform.log.info('set Remote Key Pressed: ARROW_RIGHT'); + controlCursor('right'); + break; + + case this.platform.Characteristic.RemoteKey.SELECT: + this.platform.log.info('set Remote Key Pressed: SELECT'); + controlCursor('select'); + break; + + case this.platform.Characteristic.RemoteKey.BACK: + this.platform.log.info('set Remote Key Pressed: BACK'); + controlCursor('return'); + break; + + case this.platform.Characteristic.RemoteKey.EXIT: + this.platform.log.info('set Remote Key Pressed: EXIT'); + sendRemoteCode(MainZoneRemoteCode.TOP_MENU); + break; + + case this.platform.Characteristic.RemoteKey.PLAY_PAUSE: + this.platform.log.info('set Remote Key Pressed: PLAY_PAUSE'); + if (this.state.isPlaying) { + sendRemoteCode(MainZoneRemoteCode.PAUSE); + } else { + sendRemoteCode(MainZoneRemoteCode.PLAY); + } + + this.state.isPlaying = !this.state.isPlaying; + + break; + + case this.platform.Characteristic.RemoteKey.INFORMATION: + this.platform.log.info('set Remote Key Pressed: INFORMATION'); + // We'll use the info button to flick through inputs + sendRemoteCode(MainZoneRemoteCode.INPUT_FWD); + break; + + default: + this.platform.log.info('unhandled Remote Key Pressed'); + break; + } + } catch (error) { + this.platform.log.error((error as Error).message); + } } - getInputState(callback: CharacteristicGetCallback) { - this.platform.YamahaAVR.getBasicInfo().then((basicInfo) => { - const input: Input | undefined = this.state.inputs.find((input) => input.id === basicInfo.getCurrentInput()); + async setVolume(direction: CharacteristicValue) { + try { + const zoneStatusResponse = await fetch(`${this.baseApiUrl}/${this.zone}/getStatus`); + const zoneStatus = (await zoneStatusResponse.json()) as ZoneStatus; - this.platform.log.debug(`Current input: ${basicInfo.getCurrentInput()}`); + if (zoneStatus.response_code !== 0) { + throw new Error('Failed to set zone volume'); + } - if (!input) { - return; + const currentVolume = zoneStatus.volume; + const volumeStep = 5; + + let setVolumeResponse: Response; + + if (direction === 0) { + this.platform.log.info('Volume Up', currentVolume + volumeStep); + setVolumeResponse = await fetch( + `${this.baseApiUrl}/${this.zone}/setVolume?power=${currentVolume + volumeStep}`, + ); + } else { + this.platform.log.info('Volume Down', currentVolume - volumeStep); + setVolumeResponse = await fetch( + `${this.baseApiUrl}/${this.zone}/setVolume?power=${currentVolume - volumeStep}`, + ); } - this.platform.log.info(`Current input: ${input.name} (${input.id})`); + const responseJson = (await setVolumeResponse.json()) as BaseResponse; - this.state.inputs.filter((input, index) => { - if (input.id === basicInfo.getCurrentInput()) { - return callback(null, index); - } + if (responseJson.response_code !== 0) { + throw new Error('Failed to set zone volume'); + } + } catch (error) { + this.platform.log.error((error as Error).message); + } + } - return; - }); - }); + async getInputState(): Promise { + const zoneStatus = await getZoneStatus(this.platform, this.accessory, this.zone); + + if (!zoneStatus) { + return 0; + } + + this.platform.log.info(`Current ${this.zone} input: ${zoneStatus.input}`); + + return this.state.inputs.findIndex((input) => input.id === zoneStatus.input); } - setInputState(inputIdentifier: CharacteristicValue, callback: CharacteristicSetCallback) { - const input: Input = this.state.inputs[Number(inputIdentifier)]; - this.platform.log.info(`Set input: ${input.name} (${input.id})`); - this.platform.YamahaAVR.setInputTo(input.id); - callback(null); + async setInputState(inputIndex: CharacteristicValue) { + try { + if (typeof inputIndex !== 'number') { + return; + } + + const setInputResponse = await fetch( + `${this.baseApiUrl}/${this.zone}/setInput?input=${this.state.inputs[inputIndex].id}`, + ); + const responseJson = (await setInputResponse.json()) as BaseResponse; + + if (responseJson.response_code !== 0) { + throw new Error('Failed to set zone input'); + } + + this.platform.log.info(`Set input: ${this.state.inputs[inputIndex].id}`); + } catch (error) { + this.platform.log.error((error as Error).message); + } } } diff --git a/src/index.ts b/src/index.ts index 3bf5444..02ba879 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ import { API } from 'homebridge'; -import { PLATFORM_NAME } from './settings'; -import { YamahaAVRPlatform } from './platform'; +import { PLATFORM_NAME } from './settings.js'; +import { YamahaAVRPlatform } from './platform.js'; -export = (api: API) => { +export default (api: API) => { api.registerPlatform(PLATFORM_NAME, YamahaAVRPlatform); }; diff --git a/src/platform.ts b/src/platform.ts index 1cdb583..d3fae79 100644 --- a/src/platform.ts +++ b/src/platform.ts @@ -1,64 +1,165 @@ -import { API, IndependentPlatformPlugin, Logger, PlatformConfig, Service, Characteristic } from 'homebridge'; -import Yamaha from 'yamaha-nodejs'; +import { + API, + IndependentPlatformPlugin, + Logger, + PlatformConfig, + Service, + Characteristic, + PlatformAccessory, +} from 'homebridge'; +import fetch from 'node-fetch'; -import { YamahaAPI } from './types'; -import { AccessoryContext, YamahaAVRAccessory } from './accessory'; +import { YamahaAVRAccessory } from './accessory.js'; +import { YamahaVolumeAccessory } from './volumeAccessory.js'; +import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'; +import { AccessoryContext, DeviceInfo, Features, Zone } from './types.js'; +import { YamahaPureDirectAccessory } from './pureDirectAccessory.js'; export class YamahaAVRPlatform implements IndependentPlatformPlugin { public readonly Service: typeof Service = this.api.hap.Service; public readonly Characteristic: typeof Characteristic = this.api.hap.Characteristic; - public readonly YamahaAVR: YamahaAPI = new Yamaha(this.config.ip); + public readonly platformAccessories: PlatformAccessory[] = []; + public readonly externalAccessories: PlatformAccessory[] = []; constructor(public readonly log: Logger, public readonly config: PlatformConfig, public readonly api: API) { this.log.debug('Finished initializing platform:', this.config.name); this.api.on('didFinishLaunching', () => { - this.YamahaAVR.catchRequestErrors = false; + if (!this.config.ip) { + this.log.error('IP address has not been set.'); + return; + } + this.discoverAVR(); }); } - discoverAVR() { - this.YamahaAVR.getSystemConfig() - .then((systemConfig) => { - const config = { - systemId: systemConfig.YAMAHA_AV.System[0].Config[0].System_ID[0], - modelName: systemConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0], - firmwareVersion: systemConfig.YAMAHA_AV.System[0].Config[0].Version[0], - }; - - const featuresXML = systemConfig.YAMAHA_AV.System[0].Config[0].Feature_Existence[0]; - const features: string[] = []; - - for (const feature in featuresXML) { - if (!feature.includes('Zone') && featuresXML[feature].includes('1')) { - features.push(feature); - } - } - - const device = { - UUID: this.api.hap.uuid.generate(`${config.systemId}_${this.config.ip}`), - displayName: this.config.name ? this.config.name : 'Yamaha AVR', - }; - - const accessory = new this.api.platformAccessory( - device.displayName, - device.UUID, - this.api.hap.Categories.AUDIO_RECEIVER, - ); - - accessory.context = { - ...config, - features, - device, - }; - - new YamahaAVRAccessory(this, accessory); - }) - .catch(() => { - this.log.error(` - Failed to get system config from ${this.config.name}. Please verify the AVR is connected and accessible at ${this.config.ip} - `); - }); + configureAccessory(accessory: PlatformAccessory) { + this.log.info('Loading accessory from cache:', accessory.displayName); + this.platformAccessories.push(accessory); + } + + async discoverAVR() { + try { + const baseApiUrl = `http://${this.config.ip}/YamahaExtendedControl/v1`; + const deviceInfoResponse = await fetch(`${baseApiUrl}/system/getDeviceInfo`); + const deviceInfo = (await deviceInfoResponse.json()) as DeviceInfo; + + const featuresResponse = await fetch(`${baseApiUrl}/system/getFeatures`); + const features = (await featuresResponse.json()) as Features; + + if (deviceInfo.response_code !== 0) { + throw new Error(); + } + + const device: AccessoryContext['device'] = { + displayName: this.config.name ?? `Yamaha ${deviceInfo.model_name}`, + modelName: deviceInfo.model_name, + systemId: deviceInfo.system_id, + firmwareVersion: deviceInfo.system_version, + baseApiUrl, + }; + + if (this.config.enablePureDirectSwitch) { + await this.createPureDirectAccessory(device); + } + + await this.createZoneAccessories(device, 'main'); + + features.zone.length > 1 && (await this.createZoneAccessories(device, 'zone2')); + features.zone.length > 2 && (await this.createZoneAccessories(device, 'zone3')); + features.zone.length > 3 && (await this.createZoneAccessories(device, 'zone4')); + + if (this.externalAccessories.length > 0) { + this.api.publishExternalAccessories(PLUGIN_NAME, this.externalAccessories); + } + } catch { + this.log.error(` + Failed to get system config from ${this.config.name}. Please verify the AVR is connected and accessible at ${this.config.ip} + `); + } + } + + async createZoneAccessories(device, zone) { + if (zone !== 'main' && !this.config[`${zone}Enabled`]) { + return; + } + + const avrAccessory = await this.createAVRAccessory(device, zone); + this.externalAccessories.push(avrAccessory); + + if (this.config.volumeAccessoryEnabled) { + await this.createVolumeAccessory(device, zone); + } + } + + async createAVRAccessory(device: AccessoryContext['device'], zone: Zone['id']): Promise { + let uuid = `${device.systemId}_${this.config.ip}`; + + if (zone !== 'main') { + uuid = `${uuid}_${zone}`; + } + + uuid = this.api.hap.uuid.generate(uuid); + + const accessory = new this.api.platformAccessory( + `${device.displayName} ${zone}`, + uuid, + this.api.hap.Categories.AUDIO_RECEIVER, + ); + + accessory.context = { device }; + + new YamahaAVRAccessory(this, accessory, zone); + + return accessory; + } + + async createVolumeAccessory(device: AccessoryContext['device'], zone: Zone['id']): Promise { + let uuid = `${device.systemId}_${this.config.ip}_volume`; + + if (zone !== 'main') { + uuid = `${uuid}_${zone}`; + } + + uuid = this.api.hap.uuid.generate(uuid); + + const accessory = new this.api.platformAccessory( + `AVR Vol. ${zone}`, + uuid, + this.api.hap.Categories.FAN, + ); + + accessory.context = { device }; + + new YamahaVolumeAccessory(this, accessory, zone); + + const existingAccessory = this.platformAccessories.find((accessory) => accessory.UUID === uuid); + if (existingAccessory) { + this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); + } + + this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); + } + + async createPureDirectAccessory(device: AccessoryContext['device']): Promise { + const uuid = this.api.hap.uuid.generate(`${device.systemId}_${this.config.ip}_pureDirect`); + + const accessory = new this.api.platformAccessory( + 'AVR Pure Direct', + uuid, + this.api.hap.Categories.SWITCH, + ); + + accessory.context = { device }; + + new YamahaPureDirectAccessory(this, accessory); + + const existingAccessory = this.platformAccessories.find((accessory) => accessory.UUID === uuid); + if (existingAccessory) { + this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]); + } + + this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]); } } diff --git a/src/pureDirectAccessory.ts b/src/pureDirectAccessory.ts new file mode 100644 index 0000000..88f0e74 --- /dev/null +++ b/src/pureDirectAccessory.ts @@ -0,0 +1,91 @@ +import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import fetch from 'node-fetch'; + +import { YamahaAVRPlatform } from './platform.js'; +import { AccessoryContext, BaseResponse } from './types.js'; +import { getZoneStatus } from './utils/getZoneStatus.js'; + +export class YamahaPureDirectAccessory { + private baseApiUrl: AccessoryContext['device']['baseApiUrl']; + private service: Service; + + constructor( + private readonly platform: YamahaAVRPlatform, + private readonly accessory: PlatformAccessory, + ) { + // set the AVR accessory information + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic(this.platform.Characteristic.Manufacturer, 'Yamaha') + .setCharacteristic(this.platform.Characteristic.Model, this.accessory.context.device.modelName) + .setCharacteristic(this.platform.Characteristic.SerialNumber, this.accessory.context.device.systemId) + .setCharacteristic( + this.platform.Characteristic.FirmwareRevision, + `${this.accessory.context.device.firmwareVersion}`, + ); + + this.service = this.accessory.addService(this.platform.Service.Switch); + + this.baseApiUrl = this.accessory.context.device.baseApiUrl; + this.init(); + + // regularly ping the AVR to keep power/input state syncronised + setInterval(this.updateState.bind(this), 5000); + } + + async init() { + try { + await this.createService(); + } catch (err) { + this.platform.log.error(err as string); + } + } + + async createService() { + this.service + .getCharacteristic(this.platform.Characteristic.On) + .onGet(this.getState.bind(this)) + .onSet(this.setState.bind(this)); + } + + async updateState() { + const zoneStatus = await getZoneStatus(this.platform, this.accessory, 'main'); + + if (!zoneStatus) { + return; + } + + this.service.updateCharacteristic( + this.platform.Characteristic.ProgrammableSwitchOutputState, + zoneStatus.pure_direct, + ); + } + + async getSwitchEvent(): Promise { + return this.platform.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS; + } + + async getState(): Promise { + const zoneStatus = await getZoneStatus(this.platform, this.accessory, 'main'); + + if (!zoneStatus) { + return false; + } + + return zoneStatus.pure_direct; + } + + async setState(state: CharacteristicValue) { + try { + const setPureDirectResponse = await fetch(`${this.baseApiUrl}/${'main'}/setPureDirect?enable=${state}`); + + const responseJson = (await setPureDirectResponse.json()) as BaseResponse; + + if (responseJson.response_code !== 0) { + throw new Error('Failed to set pure direct'); + } + } catch (error) { + this.platform.log.error((error as Error).message); + } + } +} diff --git a/src/storageService.ts b/src/storageService.ts index 0854a58..5bc570c 100644 --- a/src/storageService.ts +++ b/src/storageService.ts @@ -1,5 +1,5 @@ -import * as fs from 'fs-extra'; -import * as path from 'path'; +import fs from 'fs-extra'; +import path from 'path'; export class StorageService { constructor(public baseDirectory: string) {} diff --git a/src/types.ts b/src/types.ts index f2f10b4..4b7b8c9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,41 +1,296 @@ -export interface YamahaAPI { - getBasicInfo: () => Promise<{ - getVolume: () => number; - getCurrentInput: () => string; - }>; - getSystemConfig: () => Promise<{ - YAMAHA_AV: { - System: { - Config: { - Model_Name: string[]; - System_ID: string[]; - Version: string[]; - Feature_Existence: { - [key: string]: string[]; - }[]; - Name: { - Input: string[]; - }; - }[]; - }[]; - }; - }>; - getAvailableFeatures: () => Promise; - getAvailableInputsWithNames: () => Promise< - { - id: string; - name: string; - }[][] - >; - isOn: () => Promise; - powerOn: () => Promise; - powerOff: () => Promise; - volumeUp: (number) => Promise; - volumeDown: (number) => Promise; - setInputTo: (string) => Promise; - rewind: () => Promise; - skip: () => Promise; - pause: () => Promise; - play: () => Promise; - catchRequestErrors: boolean; +export interface BaseResponse { + response_code: number; +} + +export interface DeviceInfo { + response_code: number; + model_name: string; + destination: string; + device_id: string; + system_id: string; + system_version: number; + api_version: number; + netmodule_generation: number; + netmodule_version: string; + netmodule_checksum: string; + serial_number: string; + category_code: number; + operation_mode: string; + update_error_code: string; + net_module_num: number; + update_data_type: number; +} + +export interface Features { + response_code: number; + system: { + zone_num: number; + }; + zone: FeatureZone[]; +} + +export interface FeatureZone { + id: Zone['id']; + input_list: Input['id'][]; +} + +export interface ZoneStatus { + response_code: number; + power: 'on' | 'standby'; + sleep: number; + volume: number; + mute: boolean; + max_volume: number; + input: Input['id']; + input_text: Input['text']; + distribution_enable: boolean; + sound_program: SoundProgram['id']; + surr_decoder_type: string; + pure_direct: boolean; + enhancer: boolean; + tone_control: { + mode: string; + bass: number; + treble: number; + }; + dialogue_level: number; + dialogue_lift: number; + subwoofer_volume: number; + link_control: string; + link_audio_delay: string; + disable_flags: number; + contents_display: boolean; + actual_volume: { + mode: string; + value: number; + unit: string; + }; + party_enable: boolean; + extra_bass: boolean; + adaptive_drc: boolean; + dts_dialogue_control: number; + adaptive_dsp_level: boolean; +} + +export interface Zone { + id: 'main' | 'zone2' | 'zone3' | 'zone4'; + text: string; +} + +export interface Input { + id: + | 'cd' + | 'tuner' + | 'multi_ch' + | 'phono' + | 'hdmi1' + | 'hdmi2' + | 'hdmi3' + | 'hdmi4' + | 'hdmi5' + | 'hdmi6' + | 'hdmi7' + | 'hdmi8' + | 'hdmi' + | 'av1' + | 'av2' + | 'av3' + | 'av4' + | 'av5' + | 'av6' + | 'av7' + | 'v_aux' + | 'aux1' + | 'aux2' + | 'aux' + | 'audio1' + | 'audio2' + | 'audio3' + | 'audio4' + | 'audio_cd' + | 'audio' + | 'optical1' + | 'optical2' + | 'optical' + | 'coaxial1' + | 'coaxial2' + | 'coaxial' + | 'digital1' + | 'digital2' + | 'digital' + | 'line1' + | 'line2' + | 'line3' + | 'line_cd' + | 'analog' + | 'tv' + | 'bd_dvd' + | 'usb_dac' + | 'usb' + | 'bluetooth' + | 'server' + | 'net_radio' + | 'rhapsody' + | 'napster' + | 'pandora' + | 'siriusxm' + | 'spotify' + | 'juke' + | 'airplay' + | 'radiko' + | 'qobuz' + | 'mc_link' + | 'main_sync' + | 'none'; + text: string; +} + +export interface SoundProgram { + id: + | 'munich_a' + | 'munich_b' + | 'munich' + | 'frankfurt' + | 'stuttgart' + | 'vienna' + | 'amsterdam' + | 'usa_a' + | 'usa_b /tokyo' + | 'freiburg' + | 'royaumont' + | 'chamber' + | 'concert' + | 'village_gate' + | 'village_vanguard /warehouse_loft' + | 'cellar_club' + | 'jazz_club' + | 'roxy_theatre' + | 'bottom_line' + | 'arena' + | 'sports /action_game' + | 'roleplaying_game' + | 'game' + | 'music_video' + | 'music' + | 'recital_opera' + | 'pavilion /disco' + | 'standard' + | 'spectacle' + | 'sci-fi' + | 'adventure' + | 'drama' + | 'talk_show' + | 'tv_program /mono_movie' + | 'movie' + | 'enhanced' + | '2ch_stereo' + | '5ch_stereo' + | '7ch_stereo' + | '9ch_stereo /11ch_stereo' + | 'stereo' + | 'surr_decoder' + | 'my_surround' + | 'target' + | 'straight' + | 'off'; + text: string; +} + +export interface NameText { + response_code: number; + zone_list: Zone[]; + input_list: Input[]; + sound_program_list: SoundProgram[]; +} + +export interface AccessoryContext { + device: { + displayName: string; + modelName: DeviceInfo['model_name']; + systemId: DeviceInfo['system_id']; + firmwareVersion: DeviceInfo['system_version']; + baseApiUrl: string; + }; +} + +export type Cursor = 'up' | 'down' | 'left' | 'right' | 'select' | 'return'; + +export enum MainZoneRemoteCode { + // numeric codes + NUM_1 = '7F0151AE', + NUM_2 = '7F0152AD', + NUM_3 = '7F0153AC', + NUM_4 = '7F0154AB', + NUM_5 = '7F0155AA', + NUM_6 = '7F0156A9', + NUM_7 = '7F0157A8', + NUM_8 = '7F0158A7', + NUM_9 = '7F0159A6', + NUM_0 = '7F015AA5', + NUM_10_PLUS = '7F015BA4', + ENT = '7F015CA3', + + // operations codes + PLAY = '7F016897', + STOP = '7F016996', + PAUSE = '7F016798', + SEARCH_BACK = '7F016A95', + SEARCH_FWD = '7F016B94', + SKIP_BACK = '7F016C93', + SKIP_FWD = '7F016D92', + INPUT_BACK = '7A85235C', + INPUT_FWD = '7A851F60', + FM = '7F015827', + AM = '7F01552A', + + // cursor codes + UP = '7A859D62', + DOWN = '7A859C63', + LEFT = '7A859F60', + RIGHT = '7A859E61', + ENTER = '7A85DE21', + RETURN = '7A85AA55', + LEVEL = '7A858679', + ON_SCREEN = '7A85847B', + OPTION = '7A856B14', + TOP_MENU = '7A85A0DF', + POP_UP_MENU = '7A85A4DB', +} + +export enum Zone2RemoteCode { + // numeric codes + NUM_1 = '7F01718F', + NUM_2 = '7F01728C', + NUM_3 = '7F01738D', + NUM_4 = '7F01748A', + NUM_5 = '7F01758B', + NUM_6 = '7F017688', + NUM_7 = '7F017789', + NUM_8 = '7F017886', + NUM_9 = '7F017986', + NUM_0 = '7F017A84', + NUM_10_PLUS = '7F017B85', + ENT = '7F017C82', + + // operations codes + PLAY = '7F018876', + STOP = '7F018977', + PAUSE = '7F018779', + SEARCH_BACK = '7F018A74', + SEARCH_FWD = '7F018B75', + SKIP_BACK = '7F018C72', + SKIP_FWD = '7F018D73', + FM = '7F015927', + AM = '7F015628', + + // cursor codes + UP = '7A852B55', + DOWN = '7A852C52', + LEFT = '7A852D53', + RIGHT = '7A852E50', + ENTER = '7A852F51', + RETURN = '7A853C42', + OPTION = '7A856C12', + TOP_MENU = '7A85A1DF', + POP_UP_MENU = '7A85A5DB', } diff --git a/src/utils/getZoneStatus.ts b/src/utils/getZoneStatus.ts new file mode 100644 index 0000000..7aafa16 --- /dev/null +++ b/src/utils/getZoneStatus.ts @@ -0,0 +1,19 @@ +import { PlatformAccessory } from 'homebridge'; +import fetch from 'node-fetch'; +import { YamahaAVRPlatform } from '../platform.js'; +import { AccessoryContext, Zone, ZoneStatus } from '../types.js'; + +export const getZoneStatus = async ( + platform: YamahaAVRPlatform, + accessory: PlatformAccessory, + zone: Zone['id'], +): Promise => { + const zoneStatusResponse = await fetch(`${accessory.context.device.baseApiUrl}/${zone}/getStatus`); + const zoneStatus = (await zoneStatusResponse.json()) as ZoneStatus; + + if (zoneStatus.response_code !== 0) { + platform.log.error('Failed to fetch zone status'); + } + + return zoneStatus; +}; diff --git a/src/volumeAccessory.ts b/src/volumeAccessory.ts new file mode 100644 index 0000000..eaec503 --- /dev/null +++ b/src/volumeAccessory.ts @@ -0,0 +1,132 @@ +import { Service, PlatformAccessory, CharacteristicValue } from 'homebridge'; +import fetch from 'node-fetch'; + +import { YamahaAVRPlatform } from './platform.js'; +import { AccessoryContext, BaseResponse, Zone } from './types.js'; +import { getZoneStatus } from './utils/getZoneStatus.js'; + +export class YamahaVolumeAccessory { + private baseApiUrl: AccessoryContext['device']['baseApiUrl']; + private service: Service; + + constructor( + private readonly platform: YamahaAVRPlatform, + private readonly accessory: PlatformAccessory, + private readonly zone: Zone['id'], + ) { + // set the AVR accessory information + this.accessory + .getService(this.platform.Service.AccessoryInformation)! + .setCharacteristic(this.platform.Characteristic.Manufacturer, 'Yamaha') + .setCharacteristic(this.platform.Characteristic.Model, this.accessory.context.device.modelName) + .setCharacteristic(this.platform.Characteristic.SerialNumber, this.accessory.context.device.systemId) + .setCharacteristic( + this.platform.Characteristic.FirmwareRevision, + `${this.accessory.context.device.firmwareVersion}`, + ); + + this.service = this.accessory.addService(this.platform.Service.Fan); + + this.baseApiUrl = this.accessory.context.device.baseApiUrl; + this.init(); + + // regularly ping the AVR to keep power/input state syncronised + setInterval(this.updateState.bind(this), 5000); + } + + async init() { + try { + await this.createService(); + } catch (err) { + this.platform.log.error(err as string); + } + } + + async createService() { + this.service.setCharacteristic(this.platform.Characteristic.On, true); + + // Mute Get/Set + this.service + .getCharacteristic(this.platform.Characteristic.On) + .onSet(this.setMute.bind(this)) + .onGet(this.getMute.bind(this)); + + // Volume Get/Set + this.service + .getCharacteristic(this.platform.Characteristic.RotationSpeed) + .onSet(this.setVolume.bind(this)) + .onGet(this.getVolume.bind(this)); + } + + async updateState() { + const zoneStatus = await getZoneStatus(this.platform, this.accessory, this.zone); + + if (!zoneStatus) { + return; + } + + this.service.updateCharacteristic(this.platform.Characteristic.On, !zoneStatus.mute); + this.service.updateCharacteristic( + this.platform.Characteristic.RotationSpeed, + (zoneStatus.volume / zoneStatus.max_volume) * 100, + ); + } + + async getMute(): Promise { + const zoneStatus = await getZoneStatus(this.platform, this.accessory, this.zone); + + if (!zoneStatus) { + return false; + } + + return !zoneStatus.mute; + } + + async setMute(state: CharacteristicValue) { + try { + const setMuteResponse = await fetch(`${this.baseApiUrl}/${this.zone}/setMute?enable=${!state}`); + + const responseJson = (await setMuteResponse.json()) as BaseResponse; + + if (responseJson.response_code !== 0) { + throw new Error('Failed to set zone mute'); + } + } catch (error) { + this.platform.log.error((error as Error).message); + } + } + + async getVolume(): Promise { + const zoneStatus = await getZoneStatus(this.platform, this.accessory, this.zone); + + if (!zoneStatus) { + return 50; + } + + return (zoneStatus.volume / zoneStatus.max_volume) * 100; + } + + async setVolume(state: CharacteristicValue) { + try { + const zoneStatus = await getZoneStatus(this.platform, this.accessory, this.zone); + + if (!zoneStatus) { + return; + } + + const setVolumeResponse = await fetch( + `${this.baseApiUrl}/${this.zone}/setVolume?volume=${((Number(state) * zoneStatus.max_volume) / 100).toFixed( + 0, + )}`, + ); + + const responseJson = (await setVolumeResponse.json()) as BaseResponse; + + if (responseJson.response_code !== 0) { + throw new Error(`Failed to set zone volume`); + } + } catch (error) { + this.platform.log.error((error as Error).message); + } + } +} diff --git a/tsconfig.json b/tsconfig.json index 97b06b2..5a04180 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "target": "ES2018", // ~node10 - "module": "commonjs", - "lib": ["es2015", "es2016", "es2017", "es2018"], + "target": "ES6", + "module": "ESNext", + "lib": ["ESNext", "DOM"], "declaration": true, "declarationMap": true, "sourceMap": true, @@ -10,8 +10,9 @@ "rootDir": "./src", "strict": true, "esModuleInterop": true, + "moduleResolution": "node", "noImplicitAny": false }, "include": ["src/"], - "exclude": ["**/*.spec.ts"] + "exclude": ["node_modules", "**/*.spec.ts"] }