mirror of
https://github.com/koush/scrypted.git
synced 2026-02-07 16:02:13 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e31b5f970 | ||
|
|
0873a72848 | ||
|
|
145c66e1c8 | ||
|
|
2b60b45113 | ||
|
|
6f63927e2f | ||
|
|
528eabdfc0 | ||
|
|
e201ea1fc1 | ||
|
|
7790810b86 | ||
|
|
e9ec78909b | ||
|
|
26245e17ca | ||
|
|
5d87a1b2dd | ||
|
|
e1efde3868 | ||
|
|
525eb028c6 | ||
|
|
520c6a62a1 | ||
|
|
6e6898ce33 | ||
|
|
1344c9112c | ||
|
|
f2148ce26a | ||
|
|
81b00195d6 | ||
|
|
8f71778f05 | ||
|
|
2e5b8d90aa | ||
|
|
780182b94a | ||
|
|
57480f7606 | ||
|
|
1478684120 | ||
|
|
223b302bed | ||
|
|
f56cef1b50 | ||
|
|
83bfa30d4b | ||
|
|
611674af46 | ||
|
|
941ea7f346 | ||
|
|
2b9c2956d6 | ||
|
|
266d5bf8a3 | ||
|
|
d0007fc7bb | ||
|
|
75f90b78eb | ||
|
|
1e8959413e |
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
8
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -27,6 +27,11 @@ Created issues that do not meet these requirements or are improperly filled out
|
||||
1. Delete this section and everything above it.
|
||||
2. Fill out the sections below.
|
||||
|
||||
** Before You Submit**
|
||||
|
||||
- [ ] I checked that my issue isn't already filed: [Search open issues](https://github.com/koush/scrypted/issues).
|
||||
- [ ] I checked the relevant camera/device and/or plugin `Log` in the `Management Console` for errors or warnings that may help identify and resolve the issue myself.
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is. The issue tracker is only for reporting bugs in Scrypted, for general support check Discord. Hardrware support requests or assistance requests will be immediately closed.
|
||||
|
||||
@@ -43,6 +48,9 @@ A clear and concise description of what you expected to happen.
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Logs**
|
||||
Include a `Log` from the device/camera in the management console (and if applicable, the affacted plugin, like HomeKit).
|
||||
|
||||
**Server (please complete the following information):**
|
||||
- OS: [e.g. Ubuntu]
|
||||
- Installation Method: [e.g. Desktop App, Docker, Local]
|
||||
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runner: [ubuntu-latest, macos-14, macos-13, windows-latest]
|
||||
runner: [ubuntu-latest, ubuntu-24.04-arm, macos-14, macos-13, windows-latest]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
|
||||
@@ -247,7 +247,8 @@ export function createRtspParser(options?: StreamParserOptions): RtspStreamParse
|
||||
'tcp',
|
||||
...(options?.vcodec || []),
|
||||
...(options?.acodec || []),
|
||||
'-pkt_size', '64000',
|
||||
// linux and windows seem to support 64000 but darwin is 32000?
|
||||
'-pkt_size', '32000',
|
||||
'-f', 'rtsp',
|
||||
],
|
||||
findSyncFrame(streamChunks: StreamChunk[]) {
|
||||
|
||||
@@ -55,7 +55,7 @@ services:
|
||||
# Scrypted NVR Storage (Part 3 of 3)
|
||||
|
||||
# Modify to add the additional volume for Scrypted NVR.
|
||||
# The following example would mount the /mnt/sda/video path on the host
|
||||
# The following example would mount the /mnt/media/video path on the host
|
||||
# to the /nvr path inside the docker container.
|
||||
# - /mnt/media/video:/nvr
|
||||
|
||||
|
||||
@@ -36,9 +36,8 @@ curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --yes --dea
|
||||
tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
|
||||
apt -y update
|
||||
# is there a way to get a versioned package automatically?
|
||||
apt -y install nvidia-utils-560
|
||||
apt -y install cuda-drivers
|
||||
apt -y install nvidia-container-toolkit
|
||||
|
||||
nvidia-ctk runtime configure --runtime=docker
|
||||
nvidia-ctk config --set nvidia-container-cli.no-cgroups --in-place
|
||||
systemctl restart docker
|
||||
|
||||
@@ -27,7 +27,7 @@ echo "external/werift > npm install"
|
||||
npm install
|
||||
popd
|
||||
|
||||
for directory in rtsp amcrest onvif hikvision reolink unifi-protect webrtc homekit
|
||||
for directory in rtsp ffmpeg-camera amcrest onvif hikvision reolink unifi-protect webrtc homekit
|
||||
do
|
||||
echo "$directory > npm install"
|
||||
pushd plugins/$directory
|
||||
|
||||
50
packages/client/package-lock.json
generated
50
packages/client/package-lock.json
generated
@@ -1,25 +1,25 @@
|
||||
{
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.3.9",
|
||||
"version": "1.3.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.3.9",
|
||||
"version": "1.3.10",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/types": "^0.3.92",
|
||||
"engine.io-client": "^6.6.1",
|
||||
"@scrypted/types": "^0.3.100",
|
||||
"engine.io-client": "^6.6.2",
|
||||
"follow-redirects": "^1.15.9",
|
||||
"rimraf": "^6.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ip": "^1.1.3",
|
||||
"@types/node": "^22.7.4",
|
||||
"@types/node": "^22.10.7",
|
||||
"@types/ws": "^8.5.13",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.6.2"
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
@@ -76,9 +76,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/types": {
|
||||
"version": "0.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.3.92.tgz",
|
||||
"integrity": "sha512-/M1Lg42/yoFWusj5+Lyp2S0JCiWDDWcmsjiUnTf1DahZ6/M2oZ3bwR/0KX3D9vJE79owWST1Gm0+Rdvpxuil9A==",
|
||||
"version": "0.3.100",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.3.100.tgz",
|
||||
"integrity": "sha512-s/07QCxjMWqODgWj2UpLehzeo2cGFrCA9X8mvpG3owT/+q+sb8v/UUcw9TLHGSN6yIriNhceg3i9WO07kEIT6A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
@@ -120,12 +120,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz",
|
||||
"integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==",
|
||||
"version": "22.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.7.tgz",
|
||||
"integrity": "sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
@@ -272,9 +273,10 @@
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.6.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.1.tgz",
|
||||
"integrity": "sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw==",
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.2.tgz",
|
||||
"integrity": "sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
@@ -623,10 +625,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
|
||||
"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
|
||||
"version": "5.7.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
||||
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -636,10 +639,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||
"dev": true
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/client",
|
||||
"version": "1.3.9",
|
||||
"version": "1.3.10",
|
||||
"description": "",
|
||||
"main": "dist/packages/client/src/index.js",
|
||||
"scripts": {
|
||||
@@ -13,14 +13,14 @@
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/ip": "^1.1.3",
|
||||
"@types/node": "^22.7.4",
|
||||
"@types/node": "^22.10.7",
|
||||
"@types/ws": "^8.5.13",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.6.2"
|
||||
"typescript": "^5.7.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@scrypted/types": "^0.3.92",
|
||||
"engine.io-client": "^6.6.1",
|
||||
"@scrypted/types": "^0.3.100",
|
||||
"engine.io-client": "^6.6.2",
|
||||
"follow-redirects": "^1.15.9",
|
||||
"rimraf": "^6.0.1"
|
||||
}
|
||||
|
||||
4
plugins/core/package-lock.json
generated
4
plugins/core/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/core",
|
||||
"version": "0.3.103",
|
||||
"version": "0.3.108",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/core",
|
||||
"version": "0.3.103",
|
||||
"version": "0.3.108",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/core",
|
||||
"version": "0.3.103",
|
||||
"version": "0.3.108",
|
||||
"description": "Scrypted Core plugin. Provides the UI, websocket, and engine.io APIs.",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
124
plugins/doorbird/package-lock.json
generated
124
plugins/doorbird/package-lock.json
generated
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"name": "@scrypted/doorbird",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/doorbird",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.4",
|
||||
"dependencies": {
|
||||
"doorbird": "^2.1.2"
|
||||
"doorbird": "2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/node": "^22.10.10",
|
||||
"cross-env": "^7.0.3"
|
||||
}
|
||||
},
|
||||
@@ -24,36 +24,41 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/sdk": "file:../sdk",
|
||||
"@scrypted/server": "file:../server",
|
||||
"http-auth-utils": "^5.0.1",
|
||||
"node-fetch-commonjs": "^3.1.1",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "^5.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.8",
|
||||
"@types/node": "^20.11.0",
|
||||
"monaco-editor": "^0.50.0",
|
||||
"ts-node": "^10.9.2"
|
||||
}
|
||||
},
|
||||
"../../sdk": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.3.4",
|
||||
"version": "0.3.108",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"adm-zip": "^0.4.13",
|
||||
"axios": "^1.6.5",
|
||||
"babel-loader": "^9.1.0",
|
||||
"babel-plugin-const-enum": "^1.1.0",
|
||||
"esbuild": "^0.15.9",
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
"@rollup/plugin-commonjs": "^28.0.1",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||
"@rollup/plugin-typescript": "^12.1.1",
|
||||
"@rollup/plugin-virtual": "^3.0.2",
|
||||
"adm-zip": "^0.5.16",
|
||||
"axios": "^1.7.8",
|
||||
"babel-loader": "^9.2.1",
|
||||
"babel-plugin-const-enum": "^1.2.0",
|
||||
"ncp": "^2.0.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"tmp": "^0.2.1",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "^4.9.4",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-bundle-analyzer": "^4.5.0"
|
||||
"rimraf": "^6.0.1",
|
||||
"rollup": "^4.27.4",
|
||||
"tmp": "^0.2.3",
|
||||
"ts-loader": "^9.5.1",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.6.3",
|
||||
"webpack": "^5.96.1",
|
||||
"webpack-bundle-analyzer": "^4.10.2"
|
||||
},
|
||||
"bin": {
|
||||
"scrypted-changelog": "bin/scrypted-changelog.js",
|
||||
@@ -65,11 +70,9 @@
|
||||
"scrypted-webpack": "bin/scrypted-webpack.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.11.18",
|
||||
"@types/stringify-object": "^4.0.0",
|
||||
"stringify-object": "^3.3.0",
|
||||
"ts-node": "^10.4.0",
|
||||
"typedoc": "^0.23.21"
|
||||
"@types/node": "^22.10.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typedoc": "^0.26.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/common": {
|
||||
@@ -81,10 +84,14 @@
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.15.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
|
||||
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
|
||||
"dev": true
|
||||
"version": "22.10.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz",
|
||||
"integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
@@ -92,11 +99,12 @@
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.6.5",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
|
||||
"integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
|
||||
"version": "1.7.9",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
|
||||
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.4",
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
@@ -145,10 +153,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
@@ -167,25 +176,29 @@
|
||||
}
|
||||
},
|
||||
"node_modules/doorbird": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/doorbird/-/doorbird-2.1.2.tgz",
|
||||
"integrity": "sha512-ivwwsS/nOslDnuLg3UB60Axo76w5LQuZ67mCPEeWFr5+HbGYRL7PCY3iLjWYaIakh5+IvZyFPHKR4yHAvAc1WQ==",
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/doorbird/-/doorbird-2.6.0.tgz",
|
||||
"integrity": "sha512-HZBI5uFhwEVF8JFULQlpzXXvjSHmtQMJUNWfogq6vHe3kv7mCSmg0g/TDbeV5fVvisi8w7GxKD0/PpZCrtcGOg==",
|
||||
"dependencies": {
|
||||
"axios": "^1.2.1",
|
||||
"axios": "^1.6.2",
|
||||
"chacha-js": "^2.1.1",
|
||||
"libsodium-wrappers-sumo": "^0.7.11"
|
||||
"libsodium-wrappers-sumo": "^0.7.13"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
|
||||
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
@@ -225,16 +238,16 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/libsodium-sumo": {
|
||||
"version": "0.7.11",
|
||||
"resolved": "https://registry.npmjs.org/libsodium-sumo/-/libsodium-sumo-0.7.11.tgz",
|
||||
"integrity": "sha512-bY+7ph7xpk51Ez2GbE10lXAQ5sJma6NghcIDaSPbM/G9elfrjLa0COHl/7P6Wb/JizQzl5UQontOOP1z0VwbLA=="
|
||||
"version": "0.7.15",
|
||||
"resolved": "https://registry.npmjs.org/libsodium-sumo/-/libsodium-sumo-0.7.15.tgz",
|
||||
"integrity": "sha512-5tPmqPmq8T8Nikpm1Nqj0hBHvsLFCXvdhBFV7SGOitQPZAA6jso8XoL0r4L7vmfKXr486fiQInvErHtEvizFMw=="
|
||||
},
|
||||
"node_modules/libsodium-wrappers-sumo": {
|
||||
"version": "0.7.11",
|
||||
"resolved": "https://registry.npmjs.org/libsodium-wrappers-sumo/-/libsodium-wrappers-sumo-0.7.11.tgz",
|
||||
"integrity": "sha512-DGypHOmJbB1nZn89KIfGOAkDgfv5N6SBGC3Qvmy/On0P0WD1JQvNRS/e3UL3aFF+xC0m+MYz5M+MnRnK2HMrKQ==",
|
||||
"version": "0.7.15",
|
||||
"resolved": "https://registry.npmjs.org/libsodium-wrappers-sumo/-/libsodium-wrappers-sumo-0.7.15.tgz",
|
||||
"integrity": "sha512-aSWY8wKDZh5TC7rMvEdTHoyppVq/1dTSAeAR7H6pzd6QRT3vQWcT5pGwCotLcpPEOLXX6VvqihSPkpEhYAjANA==",
|
||||
"dependencies": {
|
||||
"libsodium-sumo": "^0.7.11"
|
||||
"libsodium-sumo": "^0.7.15"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
@@ -307,6 +320,13 @@
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/doorbird",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.4",
|
||||
"scripts": {
|
||||
"scrypted-setup-project": "scrypted-setup-project",
|
||||
"prescrypted-setup-project": "scrypted-package-json",
|
||||
@@ -33,12 +33,12 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"doorbird": "^2.1.2"
|
||||
"doorbird": "2.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
"@scrypted/sdk": "file:../../sdk",
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/node": "^22.10.10",
|
||||
"cross-env": "^7.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { listenZero } from '@scrypted/common/src/listen-cluster';
|
||||
import sdk, { BinarySensor, Camera, DeviceProvider, DeviceCreator, DeviceCreatorSettings, DeviceInformation, FFmpegInput, Intercom, MediaObject, PictureOptions, ResponseMediaStreamOptions, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, Settings, VideoCamera, MotionSensor } from '@scrypted/sdk';
|
||||
import child_process, { ChildProcess } from 'child_process';
|
||||
import { ffmpegLogInitialOutput, safePrintFFmpegArguments } from "@scrypted/common/src/media-helpers";
|
||||
import net from 'net';
|
||||
import { randomBytes } from 'crypto';
|
||||
import { PassThrough, Readable } from "stream";
|
||||
import { readLength } from "@scrypted/common/src/read-stream";
|
||||
import { authHttpFetch } from "@scrypted/common/src/http-auth-fetch";
|
||||
import { ApiRingEvent, ApiMotionEvent, DoorbirdAPI } from "./doorbird-api";
|
||||
import { listenZero } from '@scrypted/common/src/listen-cluster';
|
||||
import { ffmpegLogInitialOutput, safePrintFFmpegArguments } from "@scrypted/common/src/media-helpers";
|
||||
import { readLength } from "@scrypted/common/src/read-stream";
|
||||
import sdk, { BinarySensor, Camera, DeviceCreator, DeviceCreatorSettings, DeviceInformation, DeviceProvider, FFmpegInput, Intercom, MediaObject, MotionSensor, PictureOptions, ResponseMediaStreamOptions, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, Settings, VideoCamera } from '@scrypted/sdk';
|
||||
import child_process, { ChildProcess } from 'child_process';
|
||||
import { randomBytes } from 'crypto';
|
||||
import net from 'net';
|
||||
import { PassThrough, Readable } from "stream";
|
||||
import { ApiMotionEvent, ApiRingEvent, DoorbirdAPI } from "./doorbird-api";
|
||||
|
||||
const { deviceManager, mediaManager } = sdk;
|
||||
|
||||
@@ -384,7 +384,7 @@ class DoorbirdCamera extends ScryptedDeviceBase implements Intercom, Camera, Vid
|
||||
this.console.log('Doorbird: timed out waiting for tcp client from ffmpeg');
|
||||
server.close();
|
||||
}, 30000);
|
||||
const port = await listenZero(server);
|
||||
const port = await listenZero(server, '127.0.0.1');
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "Node16",
|
||||
"target": "esnext",
|
||||
"moduleResolution": "Node16",
|
||||
"esModuleInterop": true,
|
||||
|
||||
4
plugins/hikvision/package-lock.json
generated
4
plugins/hikvision/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/hikvision",
|
||||
"version": "0.0.160",
|
||||
"version": "0.0.161",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/hikvision",
|
||||
"version": "0.0.160",
|
||||
"version": "0.0.161",
|
||||
"license": "Apache",
|
||||
"dependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/hikvision",
|
||||
"version": "0.0.160",
|
||||
"version": "0.0.161",
|
||||
"description": "Hikvision Plugin for Scrypted",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache",
|
||||
|
||||
@@ -116,11 +116,15 @@ export class HikvisionCameraAPI implements HikvisionAPI {
|
||||
}
|
||||
|
||||
async checkIsOldModel() {
|
||||
// The old Hikvision DS-7608NI-E2 doesn't support channel capability checks, and the requests cause errors
|
||||
// The old Hikvision NVRs don't support channel capability checks, and the requests cause errors
|
||||
const oldModels = [
|
||||
/DS-76098NI-E2/,
|
||||
/ERI-K104-P4/
|
||||
];
|
||||
const model = await this.checkDeviceModel();
|
||||
if (!model)
|
||||
return;
|
||||
return !!model?.match(/DS-7608NI-E2/);
|
||||
return !!oldModels.find(oldModel => model?.match(oldModel));
|
||||
}
|
||||
|
||||
async checkStreamSetup(channel: string, isOld: boolean): Promise<HikvisionCameraStreamSetup> {
|
||||
|
||||
4
plugins/homekit/package-lock.json
generated
4
plugins/homekit/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/homekit",
|
||||
"version": "1.2.62",
|
||||
"version": "1.2.63",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/homekit",
|
||||
"version": "1.2.62",
|
||||
"version": "1.2.63",
|
||||
"dependencies": {
|
||||
"@koush/werift-src": "file:../../external/werift",
|
||||
"check-disk-space": "^3.4.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/homekit",
|
||||
"version": "1.2.62",
|
||||
"version": "1.2.63",
|
||||
"description": "HomeKit Plugin for Scrypted",
|
||||
"scripts": {
|
||||
"scrypted-setup-project": "scrypted-setup-project",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import sdk, { AirQuality, AirQualitySensor, CO2Sensor, DeviceProvider, Fan, FanMode, NOXSensor, OnOff, PM10Sensor, PM25Sensor, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, VOCSensor } from "@scrypted/sdk";
|
||||
import sdk, { AirQuality, AirQualitySensor, CO2Sensor, DeviceProvider, Fan, FanMode, HumidityMode, HumiditySensor, HumiditySetting, HumiditySettingStatus, NOXSensor, OnOff, PM10Sensor, PM25Sensor, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, VOCSensor } from "@scrypted/sdk";
|
||||
import { bindCharacteristic } from "../common";
|
||||
import { Accessory, Characteristic, CharacteristicEventTypes, Service, uuid } from '../hap';
|
||||
import type { HomeKitPlugin } from "../main";
|
||||
@@ -96,24 +96,161 @@ export function addCarbonDioxideSensor(device: ScryptedDevice & CO2Sensor, acces
|
||||
return co2Service;
|
||||
}
|
||||
|
||||
export function addFan(device: ScryptedDevice & Fan & OnOff, accessory: Accessory): Service {
|
||||
if (!device.interfaces.includes(ScryptedInterface.OnOff) && !device.interfaces.includes(ScryptedInterface.Fan))
|
||||
function commonHumidifierDehumidifier(mode: HumidityMode, subtype: string, name: string, device: ScryptedDevice & HumiditySetting & HumiditySensor, accessory: Accessory): Service {
|
||||
function currentState(mode: HumidityMode) {
|
||||
switch(mode) {
|
||||
case HumidityMode.Humidify:
|
||||
return Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING;
|
||||
case HumidityMode.Dehumidify:
|
||||
return Characteristic.CurrentHumidifierDehumidifierState.DEHUMIDIFYING;
|
||||
case HumidityMode.Off:
|
||||
return Characteristic.CurrentHumidifierDehumidifierState.INACTIVE;
|
||||
default:
|
||||
return Characteristic.CurrentHumidifierDehumidifierState.IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
function targetState(mode: HumidityMode) {
|
||||
switch(mode) {
|
||||
case HumidityMode.Humidify:
|
||||
return Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER;
|
||||
case HumidityMode.Dehumidify:
|
||||
return Characteristic.TargetHumidifierDehumidifierState.DEHUMIDIFIER;
|
||||
default:
|
||||
return Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER;
|
||||
}
|
||||
}
|
||||
|
||||
const service = accessory.addService(Service.HumidifierDehumidifier, name, subtype);
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.HumiditySetting, service, Characteristic.Active,
|
||||
() => {
|
||||
if (!device.humiditySetting?.mode)
|
||||
return false;
|
||||
if (device.humiditySetting.mode === mode)
|
||||
return true;
|
||||
if (device.humiditySetting.mode === HumidityMode.Auto)
|
||||
return true;
|
||||
return false;
|
||||
});
|
||||
service.getCharacteristic(Characteristic.Active).on(CharacteristicEventTypes.SET, (value, callback) => {
|
||||
callback();
|
||||
device.setHumidity({
|
||||
mode: value ? mode : HumidityMode.Off
|
||||
});
|
||||
});
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.HumiditySensor, service, Characteristic.CurrentRelativeHumidity,
|
||||
() => device.humidity);
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.HumiditySetting, service, Characteristic.CurrentHumidifierDehumidifierState,
|
||||
() => currentState(device.humiditySetting?.activeMode));
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.HumiditySetting, service, Characteristic.TargetHumidifierDehumidifierState,
|
||||
() => targetState(device.humiditySetting?.mode));
|
||||
|
||||
service.getCharacteristic(Characteristic.TargetHumidifierDehumidifierState).on(CharacteristicEventTypes.SET, (value, callback) => {
|
||||
callback();
|
||||
device.setHumidity({
|
||||
mode: value === Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER
|
||||
? HumidityMode.Humidify
|
||||
: value === Characteristic.TargetHumidifierDehumidifierState.DEHUMIDIFIER
|
||||
? HumidityMode.Dehumidify
|
||||
: HumidityMode.Auto
|
||||
});
|
||||
});
|
||||
|
||||
function targetHumidity(setting: HumiditySettingStatus) {
|
||||
if (!setting)
|
||||
return 0;
|
||||
|
||||
if (setting?.availableModes.includes(HumidityMode.Humidify)
|
||||
&& setting?.availableModes.includes(HumidityMode.Dehumidify)) {
|
||||
if (setting?.activeMode === HumidityMode.Humidify)
|
||||
return setting?.humidifierSetpoint;
|
||||
if (setting?.activeMode === HumidityMode.Dehumidify)
|
||||
return setting?.dehumidifierSetpoint;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setting?.availableModes.includes(HumidityMode.Humidify))
|
||||
return setting?.humidifierSetpoint;
|
||||
|
||||
if (setting?.availableModes.includes(HumidityMode.Dehumidify))
|
||||
return setting?.dehumidifierSetpoint;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.HumiditySetting, service, Characteristic.TargetRelativeHumidity,
|
||||
() => targetHumidity(device.humiditySetting));
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
function addHumidifier(device: ScryptedDevice & HumiditySetting & HumiditySensor, accessory: Accessory): Service {
|
||||
var service = commonHumidifierDehumidifier(HumidityMode.Humidify, "humidifier", device.name + " Humidifier", device, accessory);
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.HumiditySetting, service, Characteristic.RelativeHumidityHumidifierThreshold,
|
||||
() => device.humiditySetting?.humidifierSetpoint);
|
||||
service.getCharacteristic(Characteristic.RelativeHumidityHumidifierThreshold).on(CharacteristicEventTypes.SET, (value, callback) => {
|
||||
callback();
|
||||
device.setHumidity({
|
||||
humidifierSetpoint: value as number,
|
||||
});
|
||||
});
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
function addDehumidifer(device: ScryptedDevice & HumiditySetting & HumiditySensor, accessory: Accessory): Service {
|
||||
var service = commonHumidifierDehumidifier(HumidityMode.Dehumidify, "dehumidifier", device.name + " Dehumidifier", device, accessory);
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.HumiditySetting, service, Characteristic.RelativeHumidityDehumidifierThreshold,
|
||||
() => device.humiditySetting?.dehumidifierSetpoint);
|
||||
service.getCharacteristic(Characteristic.RelativeHumidityDehumidifierThreshold).on(CharacteristicEventTypes.SET, (value, callback) => {
|
||||
callback();
|
||||
device.setHumidity({
|
||||
dehumidifierSetpoint: value as number,
|
||||
});
|
||||
});
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
export function addHumiditySetting(device: ScryptedDevice & HumiditySetting & HumiditySensor, accessory: Accessory): Service {
|
||||
if (!device.interfaces.includes(ScryptedInterface.HumiditySetting) && !device.interfaces.includes(ScryptedInterface.HumiditySensor))
|
||||
return undefined;
|
||||
|
||||
var service;
|
||||
|
||||
if (device.humiditySetting?.availableModes.includes(HumidityMode.Humidify)) {
|
||||
service = addHumidifier(device, accessory);
|
||||
}
|
||||
|
||||
if (device.humiditySetting?.availableModes.includes(HumidityMode.Dehumidify)) {
|
||||
service = addDehumidifer(device, accessory);
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
export function addFan(device: ScryptedDevice & Fan, accessory: Accessory): Service {
|
||||
if (!device.interfaces.includes(ScryptedInterface.Fan))
|
||||
return undefined;
|
||||
|
||||
const service = accessory.addService(Service.Fanv2, device.name);
|
||||
|
||||
if (device.interfaces.includes(ScryptedInterface.OnOff)) {
|
||||
bindCharacteristic(device, ScryptedInterface.OnOff, service, Characteristic.Active,
|
||||
() => !!device.on);
|
||||
bindCharacteristic(device, ScryptedInterface.OnOff, service, Characteristic.Active,
|
||||
() => device.fan?.active);
|
||||
|
||||
service.getCharacteristic(Characteristic.Active).on(CharacteristicEventTypes.SET, (value, callback) => {
|
||||
callback();
|
||||
if (value)
|
||||
device.turnOn();
|
||||
else
|
||||
device.turnOff();
|
||||
service.getCharacteristic(Characteristic.Active).on(CharacteristicEventTypes.SET, (value, callback) => {
|
||||
callback();
|
||||
device.setFan({
|
||||
mode: value ? FanMode.Auto : FanMode.Manual,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (device.fan?.counterClockwise !== undefined) {
|
||||
bindCharacteristic(device, ScryptedInterface.Fan, service, Characteristic.RotationDirection,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Fan, FanMode, HumidityMode, HumiditySensor, HumiditySetting, OnOff, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, TemperatureSetting, TemperatureUnit, Thermometer, ThermostatMode, AirQualitySensor, AirQuality, PM10Sensor, PM25Sensor, VOCSensor, NOXSensor, CO2Sensor } from '@scrypted/sdk';
|
||||
import { Fan, FanMode, HumidityMode, HumiditySensor, HumiditySetting, OnOff, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, TemperatureSetting, TemperatureUnit, Thermometer, ThermostatMode, AirQualitySensor, AirQuality, PM10Sensor, PM25Sensor, VOCSensor, NOXSensor, CO2Sensor, HumiditySettingStatus } from '@scrypted/sdk';
|
||||
import { addSupportedType, bindCharacteristic, DummyDevice, } from '../common';
|
||||
import { Characteristic, CharacteristicEventTypes, CharacteristicSetCallback, CharacteristicValue, Service } from '../hap';
|
||||
import { addAirQualitySensor, addCarbonDioxideSensor, addFan, makeAccessory } from './common';
|
||||
import { addAirQualitySensor, addCarbonDioxideSensor, addFan, addHumiditySetting, makeAccessory } from './common';
|
||||
import type { HomeKitPlugin } from "../main";
|
||||
|
||||
addSupportedType({
|
||||
@@ -178,72 +178,60 @@ addSupportedType({
|
||||
() => device.humidity || 0);
|
||||
}
|
||||
|
||||
if (device.interfaces.includes(ScryptedInterface.HumiditySetting) && device.interfaces.includes(ScryptedInterface.HumiditySensor)) {
|
||||
const humidityService = accessory.addService(Service.HumidifierDehumidifier);
|
||||
// add fan state to thermostat service even though it is not required or optional,
|
||||
// in order to expose to Home Assistant HomeKit Controller under their climate entity
|
||||
if (device.interfaces.includes(ScryptedInterface.Fan)) {
|
||||
bindCharacteristic(device, ScryptedInterface.Fan, service, Characteristic.TargetFanState,
|
||||
() => device.fan?.mode === FanMode.Manual
|
||||
? Characteristic.TargetFanState.MANUAL
|
||||
: Characteristic.TargetFanState.AUTO);
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.HumiditySetting, humidityService, Characteristic.Active,
|
||||
() => {
|
||||
if (!device.humiditySetting?.mode)
|
||||
return false;
|
||||
if (device.humiditySetting.mode === HumidityMode.Off)
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
humidityService.getCharacteristic(Characteristic.Active).on(CharacteristicEventTypes.SET, (value, callback) => {
|
||||
service.getCharacteristic(Characteristic.TargetFanState).on(CharacteristicEventTypes.SET, (value, callback) => {
|
||||
callback();
|
||||
device.setHumidity({
|
||||
mode: value ? HumidityMode.Auto : HumidityMode.Off
|
||||
device.setFan({
|
||||
mode: value === Characteristic.TargetFanState.MANUAL ? FanMode.Manual : FanMode.Auto,
|
||||
});
|
||||
});
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.HumiditySensor, humidityService, Characteristic.CurrentRelativeHumidity,
|
||||
() => device.humidity || 0);
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.HumiditySetting, humidityService, Characteristic.CurrentHumidifierDehumidifierState,
|
||||
() => !device.humiditySetting?.activeMode
|
||||
? Characteristic.CurrentHumidifierDehumidifierState.INACTIVE
|
||||
: device.humiditySetting.activeMode === HumidityMode.Dehumidify
|
||||
? Characteristic.CurrentHumidifierDehumidifierState.DEHUMIDIFYING
|
||||
: device.humiditySetting.activeMode === HumidityMode.Humidify
|
||||
? Characteristic.CurrentHumidifierDehumidifierState.HUMIDIFYING
|
||||
: Characteristic.CurrentHumidifierDehumidifierState.IDLE);
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.HumiditySetting, humidityService, Characteristic.TargetHumidifierDehumidifierState,
|
||||
() => !device.humiditySetting?.mode || device.humiditySetting?.mode === HumidityMode.Auto
|
||||
? Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER_OR_DEHUMIDIFIER
|
||||
: device.humiditySetting?.mode === HumidityMode.Dehumidify
|
||||
? Characteristic.TargetHumidifierDehumidifierState.DEHUMIDIFIER
|
||||
: Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER);
|
||||
humidityService.getCharacteristic(Characteristic.TargetHumidifierDehumidifierState).on(CharacteristicEventTypes.SET, (value, callback) => {
|
||||
callback();
|
||||
device.setHumidity({
|
||||
mode: value === Characteristic.TargetHumidifierDehumidifierState.HUMIDIFIER
|
||||
? HumidityMode.Humidify
|
||||
: value === Characteristic.TargetHumidifierDehumidifierState.DEHUMIDIFIER
|
||||
? HumidityMode.Dehumidify
|
||||
: HumidityMode.Auto
|
||||
});
|
||||
});
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.HumiditySetting, humidityService, Characteristic.RelativeHumidityHumidifierThreshold,
|
||||
() => device.humiditySetting?.humidifierSetpoint || 0);
|
||||
humidityService.getCharacteristic(Characteristic.RelativeHumidityHumidifierThreshold).on(CharacteristicEventTypes.SET, (value, callback) => {
|
||||
callback();
|
||||
device.setHumidity({
|
||||
humidifierSetpoint: value as number,
|
||||
});
|
||||
});
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.HumiditySetting, humidityService, Characteristic.RelativeHumidityDehumidifierThreshold,
|
||||
() => device.humiditySetting?.dehumidifierSetpoint || 0);
|
||||
humidityService.getCharacteristic(Characteristic.RelativeHumidityDehumidifierThreshold).on(CharacteristicEventTypes.SET, (value, callback) => {
|
||||
callback();
|
||||
device.setHumidity({
|
||||
dehumidifierSetpoint: value as number,
|
||||
});
|
||||
});
|
||||
bindCharacteristic(device, ScryptedInterface.Fan, service, Characteristic.CurrentFanState,
|
||||
() => !device.fan?.active
|
||||
? Characteristic.CurrentFanState.INACTIVE
|
||||
: !device.fan.speed
|
||||
? Characteristic.CurrentFanState.IDLE
|
||||
: Characteristic.CurrentFanState.BLOWING_AIR);
|
||||
}
|
||||
|
||||
// add relataive target humidity to thermostat service even though it is not required or optional,
|
||||
// in order to expose to Home Assistant HomeKit Controller under their climate entity
|
||||
if (device.interfaces.includes(ScryptedInterface.HumiditySetting)) {
|
||||
function targetHumidity(setting: HumiditySettingStatus) {
|
||||
if (!setting)
|
||||
return 0;
|
||||
|
||||
if (setting?.availableModes.includes(HumidityMode.Humidify)
|
||||
&& setting?.availableModes.includes(HumidityMode.Dehumidify)) {
|
||||
if (setting?.activeMode === HumidityMode.Humidify)
|
||||
return setting?.humidifierSetpoint;
|
||||
if (setting?.activeMode === HumidityMode.Dehumidify)
|
||||
return setting?.dehumidifierSetpoint;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (setting?.availableModes.includes(HumidityMode.Humidify))
|
||||
return setting?.humidifierSetpoint;
|
||||
|
||||
if (setting?.availableModes.includes(HumidityMode.Dehumidify))
|
||||
return setting?.dehumidifierSetpoint;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bindCharacteristic(device, ScryptedInterface.HumiditySetting, service, Characteristic.TargetRelativeHumidity,
|
||||
() => targetHumidity(device.humiditySetting));
|
||||
}
|
||||
|
||||
addHumiditySetting(device, accessory);
|
||||
addFan(device, accessory);
|
||||
addAirQualitySensor(device, accessory);
|
||||
addCarbonDioxideSensor(device, accessory);
|
||||
|
||||
4
plugins/prebuffer-mixin/package-lock.json
generated
4
plugins/prebuffer-mixin/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/prebuffer-mixin",
|
||||
"version": "0.10.43",
|
||||
"version": "0.10.45",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/prebuffer-mixin",
|
||||
"version": "0.10.43",
|
||||
"version": "0.10.45",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@scrypted/common": "file:../../common",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/prebuffer-mixin",
|
||||
"version": "0.10.43",
|
||||
"version": "0.10.45",
|
||||
"description": "Video Stream Rebroadcast, Prebuffer, and Management Plugin for Scrypted.",
|
||||
"author": "Scrypted",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
4
sdk/package-lock.json
generated
4
sdk/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.3.106",
|
||||
"version": "0.3.108",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.3.106",
|
||||
"version": "0.3.108",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/sdk",
|
||||
"version": "0.3.106",
|
||||
"version": "0.3.108",
|
||||
"description": "",
|
||||
"main": "dist/src/index.js",
|
||||
"exports": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import sdk, { ScryptedInterface, Setting, Settings, SettingValue } from ".";
|
||||
import sdk, { ScryptedDeviceType, ScryptedInterface, Setting, Settings, SettingValue } from ".";
|
||||
|
||||
const { systemManager } = sdk;
|
||||
|
||||
@@ -57,9 +57,9 @@ function parseValue(value: string | null | undefined, setting: StorageSetting, r
|
||||
return value || readDefaultValue();
|
||||
}
|
||||
|
||||
export type HideFunction = (device: any) => boolean;
|
||||
export interface StorageSetting extends Omit<Setting, 'deviceFilter'> {
|
||||
deviceFilter?: string | ((test: { id: string, deviceInterface: string, interfaces: string[], type: ScryptedDeviceType, ScryptedDeviceType: typeof ScryptedDeviceType, ScryptedInterface: typeof ScryptedInterface }) => boolean);
|
||||
|
||||
export interface StorageSetting extends Setting {
|
||||
defaultValue?: any;
|
||||
persistedDefaultValue?: any;
|
||||
onPut?: (oldValue: any, newValue: any) => void;
|
||||
@@ -140,7 +140,9 @@ export class StorageSettings<T extends string> implements Settings {
|
||||
continue;
|
||||
s.key = key;
|
||||
s.value = this.getItemInternal(key as T, s, true);
|
||||
ret.push(s);
|
||||
if (typeof s.deviceFilter === 'function')
|
||||
s.deviceFilter = s.deviceFilter.toString();
|
||||
ret.push(s as Setting);
|
||||
delete s.onPut;
|
||||
delete s.onGet;
|
||||
delete s.mapPut;
|
||||
|
||||
4
sdk/types/package-lock.json
generated
4
sdk/types/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@scrypted/types",
|
||||
"version": "0.3.98",
|
||||
"version": "0.3.100",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/types",
|
||||
"version": "0.3.98",
|
||||
"version": "0.3.100",
|
||||
"license": "ISC"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@scrypted/types",
|
||||
"version": "0.3.98",
|
||||
"version": "0.3.100",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"author": "",
|
||||
|
||||
@@ -117,6 +117,7 @@ class ScryptedInterface(str, Enum):
|
||||
BinarySensor = "BinarySensor"
|
||||
Brightness = "Brightness"
|
||||
BufferConverter = "BufferConverter"
|
||||
Buttons = "Buttons"
|
||||
Camera = "Camera"
|
||||
Charger = "Charger"
|
||||
ClusterForkInterface = "ClusterForkInterface"
|
||||
@@ -166,6 +167,7 @@ class ScryptedInterface(str, Enum):
|
||||
PM25Sensor = "PM25Sensor"
|
||||
PositionSensor = "PositionSensor"
|
||||
PowerSensor = "PowerSensor"
|
||||
PressButtons = "PressButtons"
|
||||
Program = "Program"
|
||||
PushHandler = "PushHandler"
|
||||
Readme = "Readme"
|
||||
@@ -951,7 +953,7 @@ class TamperState(TypedDict):
|
||||
pass
|
||||
|
||||
|
||||
TYPES_VERSION = "0.3.98"
|
||||
TYPES_VERSION = "0.3.100"
|
||||
|
||||
|
||||
class AirPurifier:
|
||||
@@ -1006,6 +1008,10 @@ class BufferConverter:
|
||||
pass
|
||||
|
||||
|
||||
class Buttons:
|
||||
|
||||
buttons: list[str]
|
||||
|
||||
class Camera:
|
||||
"""Camera devices can take still photos."""
|
||||
|
||||
@@ -1376,6 +1382,12 @@ class PowerSensor:
|
||||
|
||||
powerDetected: bool
|
||||
|
||||
class PressButtons:
|
||||
|
||||
async def pressButton(self, button: str) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class Program:
|
||||
|
||||
async def run(self, variables: Any = None) -> Any:
|
||||
@@ -1864,6 +1876,7 @@ class ScryptedInterfaceProperty(str, Enum):
|
||||
colorTemperature = "colorTemperature"
|
||||
rgb = "rgb"
|
||||
hsv = "hsv"
|
||||
buttons = "buttons"
|
||||
running = "running"
|
||||
paused = "paused"
|
||||
docked = "docked"
|
||||
@@ -1924,6 +1937,7 @@ class ScryptedInterfaceMethods(str, Enum):
|
||||
setColorTemperature = "setColorTemperature"
|
||||
setRgb = "setRgb"
|
||||
setHsv = "setHsv"
|
||||
pressButton = "pressButton"
|
||||
sendNotification = "sendNotification"
|
||||
start = "start"
|
||||
stop = "stop"
|
||||
@@ -2178,6 +2192,14 @@ class DeviceState:
|
||||
def hsv(self, value: ColorHsv):
|
||||
self.setScryptedProperty("hsv", value)
|
||||
|
||||
@property
|
||||
def buttons(self) -> list[str]:
|
||||
return self.getScryptedProperty("buttons")
|
||||
|
||||
@buttons.setter
|
||||
def buttons(self, value: list[str]):
|
||||
self.setScryptedProperty("buttons", value)
|
||||
|
||||
@property
|
||||
def running(self) -> bool:
|
||||
return self.getScryptedProperty("running")
|
||||
@@ -2612,6 +2634,20 @@ ScryptedInterfaceDescriptors = {
|
||||
"hsv"
|
||||
]
|
||||
},
|
||||
"Buttons": {
|
||||
"name": "Buttons",
|
||||
"methods": [],
|
||||
"properties": [
|
||||
"buttons"
|
||||
]
|
||||
},
|
||||
"PressButtons": {
|
||||
"name": "PressButtons",
|
||||
"methods": [
|
||||
"pressButton"
|
||||
],
|
||||
"properties": []
|
||||
},
|
||||
"Notifier": {
|
||||
"name": "Notifier",
|
||||
"methods": [
|
||||
|
||||
@@ -214,6 +214,14 @@ export interface ColorHsv {
|
||||
v?: number;
|
||||
}
|
||||
|
||||
export interface Buttons {
|
||||
buttons?: ('doorbell' | string)[];
|
||||
}
|
||||
|
||||
export interface PressButtons {
|
||||
pressButton(button: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface NotificationAction {
|
||||
action: string;
|
||||
icon?: string;
|
||||
@@ -2255,6 +2263,8 @@ export enum ScryptedInterface {
|
||||
ColorSettingTemperature = "ColorSettingTemperature",
|
||||
ColorSettingRgb = "ColorSettingRgb",
|
||||
ColorSettingHsv = "ColorSettingHsv",
|
||||
Buttons = "Buttons",
|
||||
PressButtons = "PressButtons",
|
||||
Notifier = "Notifier",
|
||||
StartStop = "StartStop",
|
||||
Pause = "Pause",
|
||||
|
||||
5
server/.vscode/launch.json
vendored
5
server/.vscode/launch.json
vendored
@@ -99,11 +99,6 @@
|
||||
"env": {
|
||||
"SCRYPTED_PYTHON310_PATH": "/opt/homebrew/bin/python3.10",
|
||||
"DYLD_LIBRARY_PATH": "/usr/local/lib",
|
||||
"SCRYPTED_CLUSTER_WORKER_NAME": "Macaroni 2",
|
||||
"SCRYPTED_CLUSTER_LABELS": "@scrypted/coreml,@scrypted/tensorflow-lite,compute,compute.preferred",
|
||||
"SCRYPTED_CLUSTER_MODE": "client",
|
||||
"SCRYPTED_CLUSTER_SERVER": "192.168.2.130",
|
||||
"SCRYPTED_CLUSTER_SECRET": "swordfish",
|
||||
"SCRYPTED_CAN_RESTART": "true",
|
||||
"SCRYPTED_VOLUME": "/Users/koush/.scrypted-cluster/volume-client",
|
||||
}
|
||||
|
||||
12
server/package-lock.json
generated
12
server/package-lock.json
generated
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"name": "@scrypted/server",
|
||||
"version": "0.125.3",
|
||||
"version": "0.128.3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@scrypted/server",
|
||||
"version": "0.125.3",
|
||||
"version": "0.128.3",
|
||||
"hasInstallScript": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@scrypted/ffmpeg-static": "^6.1.0-build3",
|
||||
"@scrypted/node-pty": "^1.0.22",
|
||||
"@scrypted/types": "^0.3.92",
|
||||
"@scrypted/types": "^0.3.100",
|
||||
"adm-zip": "^0.5.16",
|
||||
"body-parser": "^1.20.3",
|
||||
"cookie-parser": "^1.4.7",
|
||||
@@ -557,9 +557,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@scrypted/types": {
|
||||
"version": "0.3.92",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.3.92.tgz",
|
||||
"integrity": "sha512-/M1Lg42/yoFWusj5+Lyp2S0JCiWDDWcmsjiUnTf1DahZ6/M2oZ3bwR/0KX3D9vJE79owWST1Gm0+Rdvpxuil9A==",
|
||||
"version": "0.3.100",
|
||||
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.3.100.tgz",
|
||||
"integrity": "sha512-s/07QCxjMWqODgWj2UpLehzeo2cGFrCA9X8mvpG3owT/+q+sb8v/UUcw9TLHGSN6yIriNhceg3i9WO07kEIT6A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@types/adm-zip": {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "@scrypted/server",
|
||||
"version": "0.127.1",
|
||||
"version": "0.128.3",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@scrypted/ffmpeg-static": "^6.1.0-build3",
|
||||
"@scrypted/node-pty": "^1.0.22",
|
||||
"@scrypted/types": "^0.3.92",
|
||||
"@scrypted/types": "^0.3.100",
|
||||
"adm-zip": "^0.5.16",
|
||||
"body-parser": "^1.20.3",
|
||||
"cookie-parser": "^1.4.7",
|
||||
|
||||
@@ -4,7 +4,6 @@ import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import process from 'process';
|
||||
import semver from 'semver';
|
||||
import { ensurePluginVolume } from "./plugin-volume";
|
||||
|
||||
export function defaultNpmExec(args: string[], options: child_process.SpawnOptions) {
|
||||
@@ -26,8 +25,16 @@ export function setNpmExecFunction(f: typeof npmExecFunction) {
|
||||
|
||||
export function getPluginNodePath(name: string) {
|
||||
const pluginVolume = ensurePluginVolume(name);
|
||||
const nodeMajorVersion = semver.parse(process.version).major;
|
||||
let nodeVersionedDirectory = `node${nodeMajorVersion}-${process.platform}-${process.arch}`;
|
||||
|
||||
const abi = process.versions.modules;
|
||||
let runtime = process.env.npm_config_runtime;
|
||||
if (!runtime && process.versions.electron)
|
||||
runtime = 'electron';
|
||||
if (!runtime)
|
||||
runtime = 'node';
|
||||
const { platform, arch } = process;
|
||||
let nodeVersionedDirectory = `n-${runtime}-v${abi}-${platform}-${arch}`;
|
||||
|
||||
const scryptedBase = process.env.SCRYPTED_BASE_VERSION;
|
||||
if (scryptedBase)
|
||||
nodeVersionedDirectory += '-' + scryptedBase;
|
||||
@@ -98,7 +105,7 @@ export async function installOptionalDependencies(console: Console, packageJson:
|
||||
if (!de.isDirectory())
|
||||
return;
|
||||
if (de.name.startsWith('linux') || de.name.startsWith('darwin') || de.name.startsWith('win32')
|
||||
|| de.name.startsWith('python') || de.name.startsWith('node')) {
|
||||
|| de.name.startsWith('python') || de.name.startsWith('node') || de.name.startsWith('n-')) {
|
||||
console.log('Removing old dependencies:', filePath);
|
||||
try {
|
||||
await fs.promises.rm(filePath, {
|
||||
|
||||
@@ -24,6 +24,7 @@ import { NodeThreadWorker } from './runtime/node-thread-worker';
|
||||
import { prepareZip } from './runtime/node-worker-common';
|
||||
import { getBuiltinRuntimeHosts } from './runtime/runtime-host';
|
||||
import { RuntimeWorker, RuntimeWorkerOptions } from './runtime/runtime-worker';
|
||||
import { Deferred } from '../deferred';
|
||||
|
||||
const serverVersion = require('../../package.json').version;
|
||||
|
||||
@@ -291,6 +292,14 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
|
||||
forkPeer = Promise.resolve(localPeer);
|
||||
}
|
||||
|
||||
const exitDeferred = new Deferred<string>();
|
||||
runtimeWorker.on('exit', () => {
|
||||
exitDeferred.resolve('worker exited');
|
||||
});
|
||||
runtimeWorker.on('error', e => {
|
||||
exitDeferred.resolve('worker error' + e);
|
||||
});
|
||||
|
||||
// thread workers inherit main console. pipe anything else.
|
||||
if (!(runtimeWorker instanceof NodeThreadWorker)) {
|
||||
const console = options?.id ? getMixinConsole(options.id, options.nativeId) : undefined;
|
||||
@@ -299,6 +308,9 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
|
||||
|
||||
const result = (async () => {
|
||||
const threadPeer = await forkPeer;
|
||||
exitDeferred.promise.then(reason => {
|
||||
threadPeer.kill(reason);
|
||||
});
|
||||
|
||||
// todo: handle nested forks and skip wrap. this is probably buggy.
|
||||
class PluginForkAPI extends PluginAPIProxy {
|
||||
@@ -319,13 +331,7 @@ export function startPluginRemote(mainFilename: string, pluginId: string, peerSe
|
||||
|
||||
const remote = await setupPluginRemote(threadPeer, forkApi, pluginId, { serverVersion }, () => systemManager.getSystemState());
|
||||
forks.add(remote);
|
||||
runtimeWorker.on('exit', () => {
|
||||
threadPeer.kill('worker exited');
|
||||
forkApi.removeListeners();
|
||||
forks.delete(remote);
|
||||
});
|
||||
runtimeWorker.on('error', e => {
|
||||
threadPeer.kill('worker error ' + e);
|
||||
exitDeferred.promise.then(reason => {
|
||||
forkApi.removeListeners();
|
||||
forks.delete(remote);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user