mirror of
https://github.com/koush/scrypted.git
synced 2026-02-10 01:02:18 +00:00
Merge remote-tracking branch 'tuya/webrtc' into tuya
This commit is contained in:
@@ -4,7 +4,14 @@ This is a Tuya controller that integrates Tuya devices, specifically cameras, in
|
||||
|
||||
The plugin will discover all the cameras within Tuya Cloud IoT project and report them to Scrypted, including motion events, for the ones that are supported.
|
||||
|
||||
## Features
|
||||
- Supports Tuya Cameras Streaming.
|
||||
- Supports Tuya Doorbells with ring notifications.
|
||||
- Supports 2-Way communication (for devices that support WebRTC).
|
||||
|
||||
## Requirements
|
||||
|
||||
### Access Id, Access Key, and User Id
|
||||
In order to retrieve `Access Id` and `Access Key`, you must follow the guide below:
|
||||
- [Using Smart Home PaaS (TuyaSmart, SmartLife, ect...)](https://developer.tuya.com/en/docs/iot/Platform_Configuration_smarthome?id=Kamcgamwoevrx&_source=6435717a3be1bc67fdd1f6699a1a59ac)
|
||||
|
||||
@@ -12,12 +19,8 @@ In order to retrieve `Access Id` and `Access Key`, you must follow the guide bel
|
||||
|
||||
Once you have retreived both the `Access Id` and `Access Key` from the project, you can get the `User Id` by going to Tuya Cloud IoT -> Select the Project -> Devices -> Link Tuya App Account -> and then get the UID.
|
||||
|
||||
You also need to enable Messages Service in your project in order to receive real time notifications to Scrypted. (motion events, online/offline, light switch ect...) The way this is achieved is by following this [guide](https://developer.tuya.com/en/docs/iot/subscribe-mq?id=Kavqcrvckbh9h).
|
||||
### Tuya Pulsar
|
||||
You need to enable Messages Service in your project in order to receive real time notifications to Scrypted. (motion events, online/offline, light switch ect...) The way this is achieved is by following this [guide](https://developer.tuya.com/en/docs/iot/subscribe-mq?id=Kavqcrvckbh9h).
|
||||
|
||||
- You do not need to set an alert notification of your phone.
|
||||
- This might not be necessary in the future if I believe MQTT is the way to go, but in the mean time, TuyaPulse is required for this project.
|
||||
|
||||
|
||||
## TODOs
|
||||
- Fix 2-way talk for supported platforms (Can only work with WebRTC since we only get one stream with RTSPS)
|
||||
- Add support for camera doorbells (Just need to implement doorbell notification)
|
||||
|
||||
|
||||
884
plugins/tuya/package-lock.json
generated
884
plugins/tuya/package-lock.json
generated
@@ -1,17 +1,19 @@
|
||||
{
|
||||
"name": "@scrypted/tuya",
|
||||
"version": "0.0.6",
|
||||
"version": "0.0.7-beta.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/tuya",
|
||||
"version": "0.0.6",
|
||||
"version": "0.0.7-beta.2",
|
||||
"dependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"axios": "^0.27.2",
|
||||
"crypto-js": "^4.1.1",
|
||||
"mqtt": "^4.3.7",
|
||||
"mqtt-packet": "^8.1.2",
|
||||
"ws": "^8.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -36,6 +38,49 @@
|
||||
"@types/node": "^16.9.0"
|
||||
}
|
||||
},
|
||||
"../../external/werift/packages/webrtc": {
|
||||
"name": "werift",
|
||||
"version": "0.15.11",
|
||||
"extraneous": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fidm/x509": "^1.2.1",
|
||||
"@minhducsun2002/leb128": "^0.2.0",
|
||||
"@peculiar/webcrypto": "^1.4.0",
|
||||
"@peculiar/x509": "^1.7.2",
|
||||
"@shinyoshiaki/ebml-builder": "^0.0.1",
|
||||
"aes-js": "^3.1.2",
|
||||
"binary-data": "^0.6.0",
|
||||
"buffer-crc32": "^0.2.13",
|
||||
"date-fns": "^2.28.0",
|
||||
"debug": "^4.3.4",
|
||||
"elliptic": "^6.5.3",
|
||||
"int64-buffer": "^1.0.1",
|
||||
"ip": "^1.1.8",
|
||||
"jspack": "^0.0.4",
|
||||
"lodash": "^4.17.20",
|
||||
"nano-time": "^1.0.0",
|
||||
"p-cancelable": "^2.1.1",
|
||||
"rx.mini": "^1.1.0",
|
||||
"turbo-crc32": "^1.0.1",
|
||||
"tweetnacl": "^1.0.3",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/aes-js": "^3.1.1",
|
||||
"@types/buffer-crc32": "^0.2.0",
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/elliptic": "^6.4.14",
|
||||
"@types/ip": "^1.1.0",
|
||||
"@types/jest": "^28.1.3",
|
||||
"@types/lodash": "^4.14.178",
|
||||
"@types/node": "^18.0.0",
|
||||
"@types/uuid": "^8.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.0.206",
|
||||
@@ -124,6 +169,77 @@
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"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/bl": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-5.0.0.tgz",
|
||||
"integrity": "sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ==",
|
||||
"dependencies": {
|
||||
"buffer": "^6.0.3",
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
@@ -135,11 +251,55 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/commist": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz",
|
||||
"integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==",
|
||||
"dependencies": {
|
||||
"leven": "^2.1.0",
|
||||
"minimist": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||
},
|
||||
"node_modules/concat-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
||||
"engines": [
|
||||
"node >= 6.0"
|
||||
],
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.0.2",
|
||||
"typedarray": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/crypto-js": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
|
||||
"integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
@@ -148,6 +308,25 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/duplexify": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz",
|
||||
"integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==",
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.4.1",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1",
|
||||
"stream-shift": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/end-of-stream": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||
"dependencies": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
|
||||
@@ -180,6 +359,96 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/help-me": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz",
|
||||
"integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==",
|
||||
"dependencies": {
|
||||
"glob": "^7.1.6",
|
||||
"readable-stream": "^3.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"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/inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/js-sdsl": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.3.tgz",
|
||||
"integrity": "sha512-p6umEbgMJq1OL+2z6eYFj8/yHlsx+0gX2nNoSqnu0V5KZaFGBaUfvktdbm5BGrlojadQ+Hjir0rdsaTmzoyd5Q=="
|
||||
},
|
||||
"node_modules/leven": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
|
||||
"integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
@@ -199,6 +468,249 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"node_modules/mqtt": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.3.7.tgz",
|
||||
"integrity": "sha512-ew3qwG/TJRorTz47eW46vZ5oBw5MEYbQZVaEji44j5lAUSQSqIEoul7Kua/BatBW0H0kKQcC9kwUHa1qzaWHSw==",
|
||||
"dependencies": {
|
||||
"commist": "^1.0.0",
|
||||
"concat-stream": "^2.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"duplexify": "^4.1.1",
|
||||
"help-me": "^3.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"lru-cache": "^6.0.0",
|
||||
"minimist": "^1.2.5",
|
||||
"mqtt-packet": "^6.8.0",
|
||||
"number-allocator": "^1.0.9",
|
||||
"pump": "^3.0.0",
|
||||
"readable-stream": "^3.6.0",
|
||||
"reinterval": "^1.1.0",
|
||||
"rfdc": "^1.3.0",
|
||||
"split2": "^3.1.0",
|
||||
"ws": "^7.5.5",
|
||||
"xtend": "^4.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"mqtt": "bin/mqtt.js",
|
||||
"mqtt_pub": "bin/pub.js",
|
||||
"mqtt_sub": "bin/sub.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mqtt-packet": {
|
||||
"version": "8.1.2",
|
||||
"resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-8.1.2.tgz",
|
||||
"integrity": "sha512-vL1YTct+TAy0PqX3Jv8jM3JMzObH6vC/lyA0I5LtD4xvydOdIdmofrSp12PE3jajiIOUaW3XxmQekbyToXpsSw==",
|
||||
"dependencies": {
|
||||
"bl": "^5.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"process-nextick-args": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/mqtt/node_modules/bl": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
||||
"dependencies": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mqtt/node_modules/buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"node_modules/mqtt/node_modules/mqtt-packet": {
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz",
|
||||
"integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==",
|
||||
"dependencies": {
|
||||
"bl": "^4.0.2",
|
||||
"debug": "^4.1.1",
|
||||
"process-nextick-args": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/mqtt/node_modules/ws": {
|
||||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/number-allocator": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.11.tgz",
|
||||
"integrity": "sha512-ykOuVG+oGw67qwt0eW0sPaIR+ANtB58QCpVaaGLxt0QekRXDA5Q/eG7sJmFEZpIcSVdjdevmO72Z6mH258y7Hw==",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.1",
|
||||
"js-sdsl": "^4.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"node_modules/pump": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/reinterval": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz",
|
||||
"integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ=="
|
||||
},
|
||||
"node_modules/rfdc": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
|
||||
"integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
|
||||
},
|
||||
"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/split2": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
|
||||
"integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
|
||||
"dependencies": {
|
||||
"readable-stream": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stream-shift": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
|
||||
"integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ=="
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.8.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz",
|
||||
@@ -218,6 +730,19 @@
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -296,6 +821,49 @@
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
|
||||
},
|
||||
"bl": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-5.0.0.tgz",
|
||||
"integrity": "sha512-8vxFNZ0pflFfi0WXA3WQXlj6CaMEwsmh63I1CNp0q+wWv8sD0ARx1KovSQd0l2GkwrMIOyedq0EF1FxI+RCZLQ==",
|
||||
"requires": {
|
||||
"buffer": "^6.0.3",
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"requires": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
@@ -304,16 +872,68 @@
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"commist": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz",
|
||||
"integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==",
|
||||
"requires": {
|
||||
"leven": "^2.1.0",
|
||||
"minimist": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||
},
|
||||
"concat-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
|
||||
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.0.2",
|
||||
"typedarray": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"crypto-js": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
|
||||
"integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
|
||||
},
|
||||
"debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"requires": {
|
||||
"ms": "2.1.2"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
|
||||
},
|
||||
"duplexify": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz",
|
||||
"integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==",
|
||||
"requires": {
|
||||
"end-of-stream": "^1.4.1",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1",
|
||||
"stream-shift": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"end-of-stream": {
|
||||
"version": "1.4.4",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||
"requires": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
|
||||
@@ -329,6 +949,70 @@
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"help-me": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz",
|
||||
"integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==",
|
||||
"requires": {
|
||||
"glob": "^7.1.6",
|
||||
"readable-stream": "^3.6.0"
|
||||
}
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"js-sdsl": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.3.tgz",
|
||||
"integrity": "sha512-p6umEbgMJq1OL+2z6eYFj8/yHlsx+0gX2nNoSqnu0V5KZaFGBaUfvktdbm5BGrlojadQ+Hjir0rdsaTmzoyd5Q=="
|
||||
},
|
||||
"leven": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
|
||||
"integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA=="
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
@@ -342,11 +1026,207 @@
|
||||
"mime-db": "1.52.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
|
||||
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
|
||||
},
|
||||
"mqtt": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.3.7.tgz",
|
||||
"integrity": "sha512-ew3qwG/TJRorTz47eW46vZ5oBw5MEYbQZVaEji44j5lAUSQSqIEoul7Kua/BatBW0H0kKQcC9kwUHa1qzaWHSw==",
|
||||
"requires": {
|
||||
"commist": "^1.0.0",
|
||||
"concat-stream": "^2.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"duplexify": "^4.1.1",
|
||||
"help-me": "^3.0.0",
|
||||
"inherits": "^2.0.3",
|
||||
"lru-cache": "^6.0.0",
|
||||
"minimist": "^1.2.5",
|
||||
"mqtt-packet": "^6.8.0",
|
||||
"number-allocator": "^1.0.9",
|
||||
"pump": "^3.0.0",
|
||||
"readable-stream": "^3.6.0",
|
||||
"reinterval": "^1.1.0",
|
||||
"rfdc": "^1.3.0",
|
||||
"split2": "^3.1.0",
|
||||
"ws": "^7.5.5",
|
||||
"xtend": "^4.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"bl": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
|
||||
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
|
||||
"requires": {
|
||||
"buffer": "^5.5.0",
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"requires": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"mqtt-packet": {
|
||||
"version": "6.10.0",
|
||||
"resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz",
|
||||
"integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==",
|
||||
"requires": {
|
||||
"bl": "^4.0.2",
|
||||
"debug": "^4.1.1",
|
||||
"process-nextick-args": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"mqtt-packet": {
|
||||
"version": "8.1.2",
|
||||
"resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-8.1.2.tgz",
|
||||
"integrity": "sha512-vL1YTct+TAy0PqX3Jv8jM3JMzObH6vC/lyA0I5LtD4xvydOdIdmofrSp12PE3jajiIOUaW3XxmQekbyToXpsSw==",
|
||||
"requires": {
|
||||
"bl": "^5.0.0",
|
||||
"debug": "^4.1.1",
|
||||
"process-nextick-args": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"number-allocator": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.11.tgz",
|
||||
"integrity": "sha512-ykOuVG+oGw67qwt0eW0sPaIR+ANtB58QCpVaaGLxt0QekRXDA5Q/eG7sJmFEZpIcSVdjdevmO72Z6mH258y7Hw==",
|
||||
"requires": {
|
||||
"debug": "^4.3.1",
|
||||
"js-sdsl": "^4.1.3"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
|
||||
},
|
||||
"pump": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||
"requires": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"reinterval": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz",
|
||||
"integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ=="
|
||||
},
|
||||
"rfdc": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
|
||||
"integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="
|
||||
},
|
||||
"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=="
|
||||
},
|
||||
"split2": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
|
||||
"integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
|
||||
"requires": {
|
||||
"readable-stream": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"stream-shift": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
|
||||
"integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ=="
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"typedarray": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.8.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz",
|
||||
"integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==",
|
||||
"requires": {}
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
],
|
||||
"pluginDependencies": [
|
||||
"@scrypted/prebuffer-mixin",
|
||||
"@scrypted/snapshot"
|
||||
"@scrypted/snapshot",
|
||||
"@scrypted/webrtc"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -35,6 +36,8 @@
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"axios": "^0.27.2",
|
||||
"crypto-js": "^4.1.1",
|
||||
"mqtt": "^4.3.7",
|
||||
"mqtt-packet": "^8.1.2",
|
||||
"ws": "^8.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -43,5 +46,5 @@
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/ws": "^8.5.3"
|
||||
},
|
||||
"version": "0.0.6"
|
||||
"version": "0.0.7-beta.2"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { ScryptedDeviceBase, VideoCamera, MotionSensor, BinarySensor, MediaObject, ScryptedInterface, MediaStreamOptions, MediaStreamUrl, ScryptedMimeTypes, ResponseMediaStreamOptions, OnOff, DeviceProvider, Online, Logger, Intercom } from "@scrypted/sdk";
|
||||
import sdk from '@scrypted/sdk';
|
||||
import sdk, { ScryptedDeviceBase, VideoCamera, MotionSensor, BinarySensor, MediaObject, MediaStreamOptions, MediaStreamUrl, ScryptedMimeTypes, ResponseMediaStreamOptions, OnOff, DeviceProvider, Online, Logger, Intercom, RTCSignalingClient, RTCSignalingSession, RTCAVSignalingSetup, RTCSignalingOptions, RTCSignalingSendIceCandidate, RTCSignalingChannel, RTCSessionControl } from "@scrypted/sdk";
|
||||
import { connectRTCSignalingClients } from '@scrypted/common/src/rtc-signaling';
|
||||
import { TuyaController } from "./main";
|
||||
import { TuyaDeviceConfig } from "./tuya/const";
|
||||
import { MQTTConfig, TuyaDeviceConfig, DeviceWebRTConfig, WebRTCMQMessage, OfferMessage, CandidateMessage, AnswerMessage } from "./tuya/const";
|
||||
import { TuyaDevice } from "./tuya/device";
|
||||
import { TuyaMQ } from "./tuya/mq";
|
||||
import { randomUUID } from "crypto";
|
||||
const { deviceManager } = sdk;
|
||||
|
||||
export class TuyaCameraLight extends ScryptedDeviceBase implements OnOff, Online {
|
||||
@@ -51,7 +53,174 @@ export class TuyaCameraLight extends ScryptedDeviceBase implements OnOff, Online
|
||||
}
|
||||
}
|
||||
|
||||
export class TuyaCamera extends ScryptedDeviceBase implements DeviceProvider, VideoCamera, BinarySensor, MotionSensor, OnOff, Online {
|
||||
class TuyaRTCSessionControl implements RTCSessionControl {
|
||||
|
||||
constructor(
|
||||
private readonly sessionId: string,
|
||||
private mqtt: TuyaMQ,
|
||||
private readonly mqttWebRTConfig: MQTTConfig,
|
||||
private readonly deviceWebRTConfig: DeviceWebRTConfig,
|
||||
) {
|
||||
}
|
||||
|
||||
async getRefreshAt(): Promise<number | void> {}
|
||||
|
||||
async extendSession(): Promise<void> {}
|
||||
|
||||
async setPlayback(options: { audio: boolean; video: boolean; }): Promise<void> {}
|
||||
|
||||
async endSession(): Promise<void> {
|
||||
let webRTCMessage: WebRTCMQMessage = {
|
||||
protocol: 302,
|
||||
pv: "2.2",
|
||||
t: Date.now(),
|
||||
data: {
|
||||
header: {
|
||||
type: 'disconnect',
|
||||
from: this.mqttWebRTConfig.source_topic.split('/')[3],
|
||||
to: this.deviceWebRTConfig.id,
|
||||
sub_dev_id: '',
|
||||
sessionid: this.sessionId,
|
||||
moto_id: this.deviceWebRTConfig.moto_id,
|
||||
tid: ''
|
||||
},
|
||||
msg: {
|
||||
mode: 'webrtc'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.mqtt.publish(JSON.stringify(webRTCMessage));
|
||||
}
|
||||
}
|
||||
|
||||
class TuyaRTCSignalingSesion implements RTCSignalingSession {
|
||||
private readonly sessionId: string;
|
||||
|
||||
constructor(
|
||||
private mqtt: TuyaMQ,
|
||||
private readonly mqttWebRTConfig: MQTTConfig,
|
||||
private readonly deviceWebRTConfig: DeviceWebRTConfig,
|
||||
private readonly log: Logger
|
||||
) {
|
||||
this.sessionId = randomUUID();
|
||||
}
|
||||
|
||||
async createLocalDescription(type: "offer" | "answer", setup: RTCAVSignalingSetup, sendIceCandidate: RTCSignalingSendIceCandidate): Promise<RTCSessionDescriptionInit> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (type !== 'answer')
|
||||
reject(Error('[WebRTC] - Can only create answer value.'));
|
||||
|
||||
const messageHandler = (_client: any, message: any) => {
|
||||
const webRTCMessage = JSON.parse(message) as WebRTCMQMessage;
|
||||
|
||||
this.log.i(`[WebRTC] - TuyaMQ message received: ${JSON.stringify(webRTCMessage)}`);
|
||||
|
||||
if (webRTCMessage.data.header.type == 'answer') {
|
||||
const answer = webRTCMessage.data.msg as AnswerMessage;
|
||||
resolve({
|
||||
sdp: answer.sdp,
|
||||
type: 'answer'
|
||||
});
|
||||
} else if (webRTCMessage.data.header.type == 'candidate') {
|
||||
const candidate = webRTCMessage.data.msg as CandidateMessage;
|
||||
if (!candidate?.candidate || candidate.candidate == '') {
|
||||
return;
|
||||
}
|
||||
|
||||
sendIceCandidate({
|
||||
candidate: candidate.candidate,
|
||||
sdpMid: '0',
|
||||
sdpMLineIndex: 0
|
||||
});
|
||||
} else {
|
||||
this.log.e('[WebRTC] - TuyaMQ: There was an error trying to get an answer or candidate from TuyaMQ.');
|
||||
this.mqtt.removeMessageListener(messageHandler);
|
||||
this.mqtt.stop();
|
||||
reject(new Error('[WebRTC] - TuyaMQ: There was an error trying to get an answer or candidate from TuyaMQ.'));
|
||||
}
|
||||
}
|
||||
|
||||
this.mqtt.message(messageHandler);
|
||||
});
|
||||
}
|
||||
|
||||
async setRemoteDescription(description: RTCSessionDescriptionInit, setup: RTCAVSignalingSetup): Promise<void> {
|
||||
if (description.type !== 'offer')
|
||||
throw new Error("This only accepts offer request.");
|
||||
|
||||
let offerMessage: OfferMessage = {
|
||||
mode: 'webrtc',
|
||||
sdp: description.sdp,
|
||||
auth: this.deviceWebRTConfig.auth,
|
||||
stream_type: 1
|
||||
}
|
||||
|
||||
let webRTCMessage: WebRTCMQMessage = {
|
||||
protocol: 302,
|
||||
pv: "2.2",
|
||||
t: Date.now(),
|
||||
data: {
|
||||
header: {
|
||||
type: 'offer',
|
||||
from: this.mqttWebRTConfig.source_topic.split('/')[3],
|
||||
to: this.deviceWebRTConfig.id,
|
||||
sub_dev_id: '',
|
||||
sessionid: this.sessionId,
|
||||
moto_id: this.deviceWebRTConfig.moto_id,
|
||||
tid: ''
|
||||
},
|
||||
msg: offerMessage
|
||||
}
|
||||
};
|
||||
|
||||
this.mqtt.publish(JSON.stringify(webRTCMessage));
|
||||
this.log.i(`[WebRTC] - TuyaMQ: Sent Offer w/ sdp.`);
|
||||
}
|
||||
|
||||
async addIceCandidate(candidate: RTCIceCandidateInit): Promise<void> {
|
||||
const acandidate = candidate.candidate ? `a=${candidate.candidate}` : '';
|
||||
|
||||
let candidateMessage: CandidateMessage = {
|
||||
mode: 'webrtc',
|
||||
candidate: acandidate
|
||||
}
|
||||
|
||||
let webRTCMQMessage: WebRTCMQMessage = {
|
||||
protocol: 302,
|
||||
pv: '2.2',
|
||||
t: Date.now(),
|
||||
data: {
|
||||
header: {
|
||||
type: 'candidate',
|
||||
from: this.mqttWebRTConfig.source_topic.split('/')[3],
|
||||
to: this.deviceWebRTConfig.id,
|
||||
sub_dev_id: '',
|
||||
sessionid: this.sessionId,
|
||||
moto_id: this.deviceWebRTConfig.moto_id,
|
||||
tid: ''
|
||||
},
|
||||
msg: candidateMessage
|
||||
}
|
||||
};
|
||||
|
||||
this.mqtt.publish(JSON.stringify(webRTCMQMessage));
|
||||
this.log.i(`[WebRTC] - TuyaMQ: Sent candidate.`);
|
||||
}
|
||||
|
||||
async getOptions(): Promise<RTCSignalingOptions> {
|
||||
return {
|
||||
requiresOffer: true,
|
||||
disableTrickle: false
|
||||
};
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this.sessionId;
|
||||
}
|
||||
}
|
||||
|
||||
export class TuyaCamera extends ScryptedDeviceBase implements DeviceProvider, VideoCamera, BinarySensor, MotionSensor, OnOff, Online, RTCSignalingChannel {
|
||||
private cameraLightSwitch?: TuyaCameraLight
|
||||
private previousMotion?: any;
|
||||
private previousDoorbellRing?: any;
|
||||
@@ -124,19 +293,19 @@ export class TuyaCamera extends ScryptedDeviceBase implements DeviceProvider, Vi
|
||||
const camera = this.findCamera();
|
||||
|
||||
if (!camera) {
|
||||
this.logger.w(`Could not find camera for ${this.name} to show stream.`);
|
||||
this.logger.e(`Could not find camera for ${this.name} to show stream.`);
|
||||
throw new Error(`Failed to stream ${this.name}: Camera not found.`);
|
||||
}
|
||||
|
||||
if (!camera.online) {
|
||||
this.logger.w(`${this.name} is currently offline. Will not be able to stream until device is back online.`);
|
||||
this.logger.e(`${this.name} is currently offline. Will not be able to stream until device is back online.`);
|
||||
throw new Error(`Failed to stream ${this.name}: Camera is offline.`);
|
||||
}
|
||||
|
||||
const rtsps = await this.controller.cloud?.getRTSPS(camera);
|
||||
|
||||
if (!rtsps) {
|
||||
this.logger.w("There was an error retreiving camera's rtsps for streamimg.");
|
||||
this.logger.e("There was an error retreiving camera's rtsps for streamimg.");
|
||||
throw new Error(`Failed to capture stream for ${this.name}: RTSPS link not found.`);
|
||||
}
|
||||
|
||||
@@ -148,10 +317,80 @@ export class TuyaCamera extends ScryptedDeviceBase implements DeviceProvider, Vi
|
||||
return this.createMediaObject(mediaStreamUrl, ScryptedMimeTypes.MediaStreamUrl);
|
||||
}
|
||||
|
||||
async startRTCSignalingSession(session: RTCSignalingSession): Promise<RTCSessionControl> {
|
||||
const camera = this.findCamera();
|
||||
|
||||
if (!camera) {
|
||||
this.logger.e(`Could not find camera for ${this.name} to create rtc signal session.`);
|
||||
throw new Error(`Failed to create rtc config for ${this.name}: Camera not found.`);
|
||||
}
|
||||
|
||||
const deviceWebRTConfigResponse = await this.controller.cloud?.getDeviceWebRTConfig(camera);
|
||||
|
||||
if (!deviceWebRTConfigResponse?.success) {
|
||||
this.logger.e(`[${this.name}] There was an error retrieving WebRTConfig.`);
|
||||
throw new Error(`Failed to create device rtc config for ${this.name}: request failed: ${deviceWebRTConfigResponse?.result}.`);
|
||||
}
|
||||
|
||||
const deviceWebRTConfig = deviceWebRTConfigResponse.result;
|
||||
|
||||
let mqResponse = await this.controller.cloud?.getWebRTCMQConfig(deviceWebRTConfig);
|
||||
if (!mqResponse?.success) {
|
||||
this.logger.e(`[${this.name}] There was an error retrieving WebRTC MQTT RTC Config.`);
|
||||
throw new Error(`Failed to create rtc mqtt config for ${this.name}: request failed: ${mqResponse?.result}.`);
|
||||
}
|
||||
|
||||
const mqttWebRTConfig = mqResponse.result;
|
||||
|
||||
const mqtt = new TuyaMQ(mqttWebRTConfig);
|
||||
await mqtt.connect();
|
||||
|
||||
const tuyaSignalingSession = new TuyaRTCSignalingSesion(mqtt, mqttWebRTConfig, deviceWebRTConfig, this.logger);
|
||||
|
||||
const iceServers = deviceWebRTConfig.p2p_config.ices.map((ice): RTCIceServer => {
|
||||
return {
|
||||
credential: ice.credential,
|
||||
urls: ice.urls,
|
||||
username: ice.username
|
||||
}
|
||||
});
|
||||
|
||||
const offerSetup: RTCAVSignalingSetup = {
|
||||
type: "offer",
|
||||
configuration: {
|
||||
iceServers: iceServers
|
||||
},
|
||||
audio: {
|
||||
direction: 'sendrecv',
|
||||
},
|
||||
video: {
|
||||
direction: 'recvonly',
|
||||
},
|
||||
}
|
||||
|
||||
const tuyaAnswerSetup: Partial<RTCAVSignalingSetup> = {}
|
||||
|
||||
await connectRTCSignalingClients(
|
||||
this.console,
|
||||
session,
|
||||
offerSetup,
|
||||
tuyaSignalingSession,
|
||||
tuyaAnswerSetup
|
||||
);
|
||||
|
||||
return new TuyaRTCSessionControl(
|
||||
tuyaSignalingSession.id,
|
||||
mqtt,
|
||||
mqttWebRTConfig,
|
||||
deviceWebRTConfig
|
||||
);
|
||||
}
|
||||
|
||||
async getVideoStreamOptions(): Promise<ResponseMediaStreamOptions[]> {
|
||||
return [
|
||||
{
|
||||
id: 'default',
|
||||
id: 'cloud-rtsp',
|
||||
name: 'Cloud RTSP',
|
||||
container: 'rtsp',
|
||||
video: {
|
||||
codec: 'h264',
|
||||
@@ -221,7 +460,7 @@ export class TuyaCamera extends ScryptedDeviceBase implements DeviceProvider, Vi
|
||||
} else if (this.previousMotion !== motionDetectedStatus.value) {
|
||||
this.previousMotion = motionDetectedStatus.value;
|
||||
this.triggerMotion();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +477,8 @@ export class TuyaCamera extends ScryptedDeviceBase implements DeviceProvider, Vi
|
||||
}
|
||||
|
||||
// By the time this is called, scrypted would have already reported the device
|
||||
// Only set light switch on cameras that have a status light indicator.
|
||||
// Only set light switch on cameras that have a light switch.
|
||||
|
||||
if (TuyaDevice.hasLightSwitch(camera)) {
|
||||
this.getDevice(this.nativeLightSwitchId)?.updateState(camera);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Device, DeviceDiscovery, DeviceProvider, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, Setting, Settings, SettingValue } from '@scrypted/sdk';
|
||||
import { Device, DeviceDiscovery, DeviceProvider, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, Setting, Settings } from '@scrypted/sdk';
|
||||
import sdk from '@scrypted/sdk';
|
||||
import { StorageSettings } from '../../../common/src/settings';
|
||||
import { TuyaCloud } from './tuya/cloud';
|
||||
import { TuyaDevice } from './tuya/device';
|
||||
import { createInstanceableProviderPlugin } from '@scrypted/common/src/provider-plugin';
|
||||
@@ -193,7 +192,7 @@ export class TuyaController extends ScryptedDeviceBase implements DeviceProvider
|
||||
let deviceInfo: string[] = [`Creating camera device for: \n- ${camera.name}`];
|
||||
|
||||
if (TuyaDevice.isDoorbell(camera)) {
|
||||
deviceInfo.push(`- Doorbell Notification`);
|
||||
deviceInfo.push(`- Doorbell Notification Supported`);
|
||||
device.interfaces.push(ScryptedInterface.BinarySensor);
|
||||
}
|
||||
|
||||
@@ -203,14 +202,19 @@ export class TuyaController extends ScryptedDeviceBase implements DeviceProvider
|
||||
}
|
||||
|
||||
if (TuyaDevice.hasMotionDetection(camera)) {
|
||||
deviceInfo.push(`- Motion Detection`);
|
||||
deviceInfo.push(`- Motion Detection Supported`);
|
||||
device.interfaces.push(ScryptedInterface.MotionSensor);
|
||||
}
|
||||
|
||||
if (await TuyaDevice.supportsWebRTC(camera, this.cloud)) {
|
||||
deviceInfo.push(`- WebRTC Supported with Intercom`);
|
||||
device.interfaces.push(ScryptedInterface.RTCSignalingChannel);
|
||||
}
|
||||
|
||||
// Device Provider
|
||||
|
||||
if (TuyaDevice.hasLightSwitch(camera)) {
|
||||
deviceInfo.push(`- Light Switch`);
|
||||
deviceInfo.push(`- Has Light Switch`);
|
||||
device.interfaces.push(ScryptedInterface.DeviceProvider);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Axios, Method } from "axios";
|
||||
import { HmacSHA256, SHA256, lib } from 'crypto-js';
|
||||
import { getTuyaCloudEndpoint, TuyaSupportedCountry } from "./utils";
|
||||
import { DeviceFunction, TuyaDeviceStatus, RTSPToken, TuyaDeviceConfig, TuyaResponse } from "./const";
|
||||
import { TuyaDeviceStatus, RTSPToken, TuyaDeviceConfig, TuyaResponse, MQTTConfig, DeviceWebRTConfig as DeviceWebRTConfig } from "./const";
|
||||
import { randomBytes } from "crypto";
|
||||
|
||||
interface Session {
|
||||
accessToken: string;
|
||||
@@ -97,7 +98,9 @@ export class TuyaCloud {
|
||||
|
||||
// Camera Functions
|
||||
|
||||
public async getRTSPS(camera: TuyaDeviceConfig): Promise<RTSPToken | undefined> {
|
||||
public async getRTSPS(
|
||||
camera: TuyaDeviceConfig
|
||||
): Promise<RTSPToken | undefined> {
|
||||
interface RTSPResponse {
|
||||
url: string
|
||||
}
|
||||
@@ -117,6 +120,39 @@ export class TuyaCloud {
|
||||
}
|
||||
}
|
||||
|
||||
public async getDeviceWebRTConfig(camera: TuyaDeviceConfig) : Promise<TuyaResponse<DeviceWebRTConfig>> {
|
||||
const response = await this.get<DeviceWebRTConfig>(
|
||||
`/v1.0/users/${this.userId}/devices/${camera.id}/webrtc-configs`
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public async getWebRTCMQConfig(webRTCDeviceConfig: DeviceWebRTConfig) : Promise<TuyaResponse<MQTTConfig>> {
|
||||
const response = await this.post<any>(
|
||||
`/v1.0/open-hub/access/config`,
|
||||
{
|
||||
link_id: randomBytes(8).readUInt8(),
|
||||
uid: this.userId,
|
||||
link_type: 'mqtt',
|
||||
topics: 'ipc'
|
||||
}
|
||||
);
|
||||
|
||||
if (response.success) {
|
||||
response.result = {
|
||||
...response.result,
|
||||
sink_topic: (response.result.sink_topic.ipc as string)
|
||||
.replace('{device_id}', webRTCDeviceConfig.id)
|
||||
.replace('moto_id', webRTCDeviceConfig.moto_id),
|
||||
source_topic: response.result.source_topic.ipc as string
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public getSessionUserId(): string | undefined {
|
||||
return this.session?.uid;
|
||||
}
|
||||
|
||||
@@ -51,22 +51,79 @@ export interface RTSPToken {
|
||||
|
||||
export interface MQTTConfig {
|
||||
url: string;
|
||||
client_id: string;
|
||||
username: string;
|
||||
password: string;
|
||||
client_id: string;
|
||||
source_topic: string;
|
||||
sink_topic: string;
|
||||
expire_topic: string;
|
||||
expire_time: number;
|
||||
}
|
||||
|
||||
// From Unify Protect Api:
|
||||
// This type declaration make all properties optional recursively including nested objects. This should
|
||||
// only be used on JSON objects only. Otherwise...you're going to end up with class methods marked as
|
||||
// optional as well. Credit for this belongs to: https://github.com/joonhocho/tsdef. #Grateful
|
||||
// export type DeepPartial<T> = {
|
||||
// [P in keyof T]?: T[P] extends Array<infer I> ? Array<DeepPartial<I>> : DeepPartial<T[P]>
|
||||
// };
|
||||
export interface DeviceWebRTConfig {
|
||||
audio_attributes: AudioAttributes;
|
||||
auth: string;
|
||||
id: string;
|
||||
moto_id: string;
|
||||
p2p_config: P2PConfig;
|
||||
skill: string;
|
||||
supports_webrtc: boolean;
|
||||
vedio_clarity: number;
|
||||
}
|
||||
|
||||
// export type ProtectTuyaDeviceConfig = Readonly<TuyaDeviceInterface>;
|
||||
// export type ProtectTuyaDeviceConfigPartial = DeepPartial<TuyaDeviceInterface>;
|
||||
// export type ProtectTuyaDeviceStatus = Readonly<TuyaDeviceStatus>;
|
||||
interface AudioAttributes {
|
||||
call_mode: number[];
|
||||
hardware_capability: number[];
|
||||
}
|
||||
|
||||
interface P2PConfig {
|
||||
ices: Ice[];
|
||||
}
|
||||
|
||||
interface Ice {
|
||||
urls: string;
|
||||
credential?: string;
|
||||
ttl?: number;
|
||||
username?: string;
|
||||
}
|
||||
|
||||
export interface WebRTCMQMessage {
|
||||
protocol: number;
|
||||
pv: string;
|
||||
t: number;
|
||||
data: {
|
||||
header: {
|
||||
type: 'offer' | 'answer' | 'candidate' | 'disconnect';
|
||||
from: string;
|
||||
to: string;
|
||||
sub_dev_id: string;
|
||||
sessionid: string;
|
||||
moto_id: string;
|
||||
tid: string;
|
||||
},
|
||||
msg: OfferMessage | AnswerMessage | CandidateMessage | DisconnectMessage
|
||||
}
|
||||
}
|
||||
|
||||
export interface OfferMessage {
|
||||
mode: string;
|
||||
sdp: string;
|
||||
auth: string;
|
||||
stream_type: number;
|
||||
}
|
||||
|
||||
export interface AnswerMessage {
|
||||
mode: string;
|
||||
sdp: string;
|
||||
stream_type: number;
|
||||
token: Ice[];
|
||||
replay: any;
|
||||
}
|
||||
|
||||
export interface CandidateMessage {
|
||||
mode: string;
|
||||
candidate: string;
|
||||
}
|
||||
|
||||
export interface DisconnectMessage {
|
||||
mode: string;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { TuyaCloud } from "./cloud";
|
||||
import { TuyaDeviceStatus, TuyaDeviceConfig as TuyaDeviceConfig } from "./const";
|
||||
|
||||
export namespace TuyaDevice {
|
||||
@@ -70,6 +71,15 @@ export namespace TuyaDevice {
|
||||
return getStatus(camera, motionDetectionCodes);
|
||||
}
|
||||
|
||||
// Supports WebRTC
|
||||
|
||||
export async function supportsWebRTC(camera: TuyaDeviceConfig, cloud: TuyaCloud) {
|
||||
const webRTConfig = await cloud.getDeviceWebRTConfig(camera);
|
||||
return webRTConfig.success && webRTConfig.result.supports_webrtc;
|
||||
}
|
||||
|
||||
// Device Status
|
||||
|
||||
function getStatus(camera: TuyaDeviceConfig, statusCode: string[]) : TuyaDeviceStatus | undefined {
|
||||
return camera.status.find(value => statusCode.includes(value.code));
|
||||
}
|
||||
|
||||
129
plugins/tuya/src/tuya/mq.ts
Normal file
129
plugins/tuya/src/tuya/mq.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import Event from 'events';
|
||||
import * as mqtt from "mqtt";
|
||||
import { IClientPublishOptions } from 'mqtt';
|
||||
import { IPublishPacket } from 'mqtt-packet'
|
||||
import { MQTTConfig } from "./const";
|
||||
|
||||
|
||||
export class TuyaMQ {
|
||||
static connected = "TUYA_CONNECTED";
|
||||
static message = "TUYA_MESSAGE";
|
||||
static error = "TUYA_ERROR";
|
||||
static close = "TUYA_CLOSE";
|
||||
|
||||
private client?: mqtt.MqttClient;
|
||||
private config: MQTTConfig;
|
||||
private event: Event;
|
||||
|
||||
constructor(
|
||||
config: MQTTConfig
|
||||
) {
|
||||
this.config = Object.assign({}, config);
|
||||
this.event = new Event();
|
||||
}
|
||||
|
||||
public stop() {
|
||||
this.client?.end();
|
||||
}
|
||||
|
||||
public async connect(): Promise<mqtt.Client> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.event.on(
|
||||
TuyaMQ.connected,
|
||||
(client: mqtt.MqttClient) => {
|
||||
if (client.connected) {
|
||||
resolve(client);
|
||||
} else {
|
||||
reject(new Error('Client did not connect successfully.'));
|
||||
}
|
||||
}
|
||||
);
|
||||
this.client = this._connect();
|
||||
});
|
||||
}
|
||||
|
||||
public message(
|
||||
cb: (client: mqtt.MqttClient, message: any) => void
|
||||
) {
|
||||
this.event.on(TuyaMQ.message, cb);
|
||||
}
|
||||
|
||||
public error(
|
||||
cb: (client: mqtt.MqttClient, error: Error) => void
|
||||
) {
|
||||
this.event.on(TuyaMQ.error, cb);
|
||||
}
|
||||
|
||||
public close(
|
||||
cb: (client: mqtt.MqttClient) => void
|
||||
) {
|
||||
this.event.on(TuyaMQ.close, cb);
|
||||
}
|
||||
|
||||
public publish(message: string) {
|
||||
const properties: IClientPublishOptions = {
|
||||
qos: 1,
|
||||
retain: false
|
||||
}
|
||||
|
||||
this.client?.publish(this.config.sink_topic, message, properties);
|
||||
}
|
||||
|
||||
public removeMessageListener(
|
||||
cb: (client: mqtt.MqttClient, message: any) => void
|
||||
) {
|
||||
this.event.removeListener(TuyaMQ.message, cb);
|
||||
}
|
||||
|
||||
private _connect() {
|
||||
this.client = mqtt.connect(this.config.url, {
|
||||
clientId: this.config.client_id,
|
||||
username: this.config.username,
|
||||
password: this.config.password
|
||||
});
|
||||
|
||||
this.subConnect(this.client);
|
||||
this.subMessage(this.client);
|
||||
this.subError(this.client);
|
||||
this.subClose(this.client);
|
||||
return this.client;
|
||||
}
|
||||
|
||||
private subConnect(client: mqtt.MqttClient) {
|
||||
client.on('connect', () => {
|
||||
client.subscribe(this.config.source_topic);
|
||||
this.event.emit(
|
||||
TuyaMQ.connected,
|
||||
client
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
private subMessage(client: mqtt.MqttClient) {
|
||||
client.on('message', (topic: string, payload: Buffer, packet: IPublishPacket) => {
|
||||
this.event.emit(
|
||||
TuyaMQ.message,
|
||||
client,
|
||||
payload
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
private subError(client: mqtt.MqttClient) {
|
||||
client.on('error', (error: Error) => {
|
||||
this.event.emit(
|
||||
TuyaMQ.error,
|
||||
error
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
private subClose(client: mqtt.MqttClient) {
|
||||
client.on('close', () => {
|
||||
this.event.emit(
|
||||
TuyaMQ.close,
|
||||
this.client
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user