diff --git a/install/local/install-scrypted-dependencies-win.ps1 b/install/local/install-scrypted-dependencies-win.ps1 index eab133c8e3..9a556dbbfc 100644 --- a/install/local/install-scrypted-dependencies-win.ps1 +++ b/install/local/install-scrypted-dependencies-win.ps1 @@ -8,7 +8,7 @@ sc.exe stop scrypted.exe iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) # Install node.js -choco upgrade -y nodejs-lts --version=18.14.0 +choco upgrade -y nodejs-lts --version=18.19.1 # Install Python choco upgrade -y python39 diff --git a/plugins/amcrest/README.md b/plugins/amcrest/README.md index 7ad7f7f4fd..33a90d9b05 100644 --- a/plugins/amcrest/README.md +++ b/plugins/amcrest/README.md @@ -39,6 +39,8 @@ Each 'Channel' or (camera) Device attached to the NVR must be configured as sepa * `Snapshot URL Override` camera's IP address (preferred) or specific port number of NVR for that camera (may work). That is: `http:///cgi-bin/snapshot.cgi` or `http://:/cgi-bin/snapshot.cgi` * `Channel Number Override` camera's channel number as known to DVR +## Dahua Lock/Unlock +Dahua DTO video intercoms have built-in access control for locks/doors. If you have set the Amcrest plugin up with `Doorbell Type` set to `Dahua Doorbell`, you can enable support for remotely locking/unlocking by enabling/toggle the option `Enable Dahua Lock`. # Troubleshooting ## General diff --git a/plugins/amcrest/package-lock.json b/plugins/amcrest/package-lock.json index df5f45d7ae..2f06882e7a 100644 --- a/plugins/amcrest/package-lock.json +++ b/plugins/amcrest/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/amcrest", - "version": "0.0.134", + "version": "0.0.135", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@scrypted/amcrest", - "version": "0.0.134", + "version": "0.0.135", "license": "Apache", "dependencies": { "@scrypted/common": "file:../../common", diff --git a/plugins/amcrest/package.json b/plugins/amcrest/package.json index 796868199c..0785de1370 100644 --- a/plugins/amcrest/package.json +++ b/plugins/amcrest/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/amcrest", - "version": "0.0.134", + "version": "0.0.135", "description": "Amcrest Plugin for Scrypted", "author": "Scrypted", "license": "Apache", diff --git a/plugins/amcrest/src/amcrest-api.ts b/plugins/amcrest/src/amcrest-api.ts index 3637708e12..d7deb075f1 100644 --- a/plugins/amcrest/src/amcrest-api.ts +++ b/plugins/amcrest/src/amcrest-api.ts @@ -125,4 +125,20 @@ export class AmcrestCameraClient { this.console.log(response.body); } } + + async unlock(): Promise { + const response = await this.request({ + url: `http://${this.ip}/cgi-bin/accessControl.cgi?action=openDoor&channel=1&UserID=101&Type=Remote`, + responseType: 'text', + }); + return response.body.includes('OK'); + } + + async lock(): Promise { + const response = await this.request({ + url: `http://${this.ip}/cgi-bin/accessControl.cgi?action=closeDoor&channel=1&UserID=101&Type=Remote`, + responseType: 'text', + }); + return response.body.includes('OK'); + } } diff --git a/plugins/amcrest/src/main.ts b/plugins/amcrest/src/main.ts index 0b4d115904..0a59a43898 100644 --- a/plugins/amcrest/src/main.ts +++ b/plugins/amcrest/src/main.ts @@ -1,6 +1,6 @@ import { ffmpegLogInitialOutput } from '@scrypted/common/src/media-helpers'; import { readLength } from "@scrypted/common/src/read-stream"; -import sdk, { Camera, DeviceCreatorSettings, DeviceInformation, FFmpegInput, Intercom, MediaObject, MediaStreamOptions, PictureOptions, Reboot, RequestPictureOptions, RequestRecordingStreamOptions, ResponseMediaStreamOptions, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, VideoCameraConfiguration, VideoRecorder } from "@scrypted/sdk"; +import sdk, { Camera, DeviceCreatorSettings, DeviceInformation, FFmpegInput, Intercom, Lock, MediaObject, MediaStreamOptions, Reboot, RequestPictureOptions, RequestRecordingStreamOptions, ResponseMediaStreamOptions, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, VideoCameraConfiguration, VideoRecorder } from "@scrypted/sdk"; import child_process, { ChildProcess } from 'child_process'; import { PassThrough, Readable, Stream } from "stream"; import { OnvifIntercom } from "../../onvif/src/onvif-intercom"; @@ -22,7 +22,7 @@ function findValue(blob: string, prefix: string, key: string) { return parts[1]; } -class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration, Camera, Intercom, VideoRecorder, Reboot { +class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration, Camera, Intercom, Lock, VideoRecorder, Reboot { eventStream: Stream; cp: ChildProcess; client: AmcrestCameraClient; @@ -270,6 +270,16 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration, if (doorbellType == DAHUA_DOORBELL_TYPE) { + ret.push( + { + title: 'Enable Dahua Lock', + key: 'enableDahuaLock', + description: 'Some Dahua Doorbells have a built in lock/door access control.', + type: 'boolean', + value: (this.storage.getItem('enableDahuaLock') === 'true').toString(), + } + ); + ret.push( { title: 'Multiple Call Buttons', @@ -462,6 +472,10 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration, if (isDoorbell || twoWayAudio) { interfaces.push(ScryptedInterface.Intercom); } + const enableDahuaLock = this.storage.getItem('enableDahuaLock') === 'true'; + if (isDoorbell && doorbellType === DAHUA_DOORBELL_TYPE && enableDahuaLock) { + interfaces.push(ScryptedInterface.Lock); + } const continuousRecording = this.storage.getItem('continuousRecording') === 'true'; if (continuousRecording) interfaces.push(ScryptedInterface.VideoRecorder); @@ -598,6 +612,18 @@ class AmcrestCamera extends RtspSmartCamera implements VideoCameraConfiguration, showRtspUrlOverride() { return false; } + + async lock(): Promise { + if (!this.client.lock()) { + this.console.error("Could not lock"); + } + } + + async unlock(): Promise { + if (!this.client.unlock()) { + this.console.error("Could not unlock"); + } + } } class AmcrestProvider extends RtspProvider { diff --git a/plugins/homekit/README.md b/plugins/homekit/README.md index 5bedafb496..254e17227f 100644 --- a/plugins/homekit/README.md +++ b/plugins/homekit/README.md @@ -32,8 +32,10 @@ If recordings dont work, it's generally because of a few reasons, **follow the s ### HomeKit Discovery and Pairing Issues -If HomeKit is not discoverable, make sure LAN/WLAN multicast is enabled on your router. -If HomeKit fails while pairing during a Docker install, ensure host networking is being used. +* Ensure all your Home hubs are online and updated. Power cycling them is recommended in case one is stuck. +* Ensure LAN/WLAN multicast is enabled on your router. +* Ensure the iOS device you are using for pairing is on the same network (pairing will fail on cellular). +* Ensure the Docker installation (if applicable) is using host networking. This configuration is the default if the official Scrypted Docker compose install script was used. ### HomeKit Live Streaming Timeout (Recordings may be working) @@ -50,6 +52,9 @@ This almost always due to your camera bitrate being too high for remote streamin 1) Use a lower bitrate substream for Remote Streaming. 2) Enable Transcoding on Remote Streaming. +Other things to check: +1) Ensure the Home app has cellular network permission. + ### mDNS Advertiser Options diff --git a/plugins/homekit/src/types/common.ts b/plugins/homekit/src/types/common.ts index bea10e0bf7..a000f4eadb 100644 --- a/plugins/homekit/src/types/common.ts +++ b/plugins/homekit/src/types/common.ts @@ -3,6 +3,7 @@ import { bindCharacteristic } from "../common"; import { Accessory, Characteristic, CharacteristicEventTypes, Service, uuid } from '../hap'; import type { HomeKitPlugin } from "../main"; import { getService as getOnOffService } from "./onoff-base"; +import { HOMEKIT_MIXIN } from "../homekit-mixin"; const { deviceManager, systemManager } = sdk; @@ -201,7 +202,7 @@ export function mergeOnOffDevicesByType(device: ScryptedDevice & DeviceProvider, const children = getChildDevices(device); const mergedDevices = []; const services = children.map((child: ScryptedDevice & OnOff) => { - if (child.type !== type || !child.interfaces.includes(ScryptedInterface.OnOff)) + if (child.type !== type || !child.interfaces.includes(ScryptedInterface.OnOff) || !child.interfaces.includes(HOMEKIT_MIXIN)) return undefined; const onOffService = getOnOffService(child, accessory, Service.Switch)