Compare commits

..

80 Commits

Author SHA1 Message Date
Koushik Dutta
0ea609c80c server: update dependencies 2023-04-07 21:14:58 -07:00
Koushik Dutta
71ee5727f1 sdk: update 2023-04-07 15:11:24 -07:00
Koushik Dutta
2383f16112 videoanalysis: improve logging for debugging 2023-04-07 14:53:27 -07:00
Koushik Dutta
7d5defd736 homekit: publish 2023-04-07 14:06:38 -07:00
Koushik Dutta
cbf4cf0579 server: fix typo in storage of undefined 2023-04-07 14:06:17 -07:00
Koushik Dutta
422dd94e5c plugins: update problematic setStorage(key, undefined) 2023-04-07 14:05:49 -07:00
Koushik Dutta
076f5e27f1 postrelease 2023-04-07 13:50:09 -07:00
Koushik Dutta
645de2e5fd predict: improve input aspect ratio match fast path 2023-04-07 10:17:05 -07:00
Koushik Dutta
dcf24a77d7 postrelease 2023-04-07 08:30:27 -07:00
Koushik Dutta
7065365a47 postrelease 2023-04-07 08:30:21 -07:00
Koushik Dutta
b82520776e sdk/server: search for TypedDict in typing and typing_extensions 2023-04-07 08:29:47 -07:00
Koushik Dutta
638c1f77fd ring: fix login issues 2023-04-07 08:06:16 -07:00
Koushik Dutta
73a489ea37 rtc: null check double offer error 2023-04-06 22:35:52 -07:00
Koushik Dutta
77d69f025a server: fix release build scripts 2023-04-06 08:32:44 -07:00
Koushik Dutta
3bc14ad248 prebeta 2023-04-06 08:32:33 -07:00
Koushik Dutta
03e5a9dec1 Merge branch 'main' of github.com:koush/scrypted 2023-04-06 08:22:11 -07:00
Koushik Dutta
57b790c332 server: publish beta 2023-04-06 08:22:05 -07:00
Koushik Dutta
ce2ea63be7 server: add hook for npm exec in non-node environment (electron) 2023-04-06 08:21:37 -07:00
Alex Leeds
2dd4721b7f ring: fix login 406 error (#698) 2023-04-06 07:45:33 -07:00
Justin Angevaare
667075dfad Add tip about digest authentication (#697) 2023-04-06 07:45:24 -07:00
Koushik Dutta
7abdb06b66 postrelease 2023-04-05 14:39:28 -07:00
Koushik Dutta
43e5822c93 server: fix first run account creation bug 2023-04-05 14:39:20 -07:00
Koushik Dutta
bc579514e7 python-codecs: add numpy to requirements.txt 2023-04-05 11:55:04 -07:00
Koushik Dutta
825100f94e webrtc: add answer only option 2023-04-05 10:17:17 -07:00
Koushik Dutta
803bfc1560 pam-diff: tweak default motion percent 2023-04-05 10:16:46 -07:00
Koushik Dutta
b2013a54ed pam-diff: tweak default motion percent 2023-04-05 10:15:40 -07:00
Koushik Dutta
f252407935 rebroadcast: fix settings clear issue 2023-04-04 11:37:37 -07:00
Koushik Dutta
516f2a2a7b server: fetch version from package registry 2023-04-04 10:14:31 -07:00
Koushik Dutta
c1677ce691 postrelease 2023-04-04 09:59:39 -07:00
Koushik Dutta
5028fb812d server: storage polyfill should serialize keys and values as strings 2023-04-04 09:58:51 -07:00
Koushik Dutta
2db4e2579f server: add more files to .npmignore 2023-04-04 08:24:23 -07:00
Koushik Dutta
b339ca6cd2 fix bug where deleted users have continued/escalated permissions 2023-04-04 08:17:44 -07:00
Koushik Dutta
f100999cb1 postrelease 2023-04-04 08:17:13 -07:00
Koushik Dutta
2863756bd6 Revert "webrtc: startRtpForwarderProcess remove werift dependency"
This reverts commit 143a0b2c41.
2023-04-03 14:26:56 -07:00
Koushik Dutta
cc408850a0 videoanalysis: changing pipeline should restart video analysis 2023-04-03 12:26:02 -07:00
Koushik Dutta
ed1ceeda51 core: return correct acls for admins 2023-04-03 11:16:14 -07:00
Koushik Dutta
df09d8e92a Merge branch 'main' of github.com:koush/scrypted 2023-04-03 08:34:35 -07:00
Koushik Dutta
298ac960d1 core: fix checkbox ui 2023-04-03 08:34:30 -07:00
Nick Berardi
62d4d55aae unifi: added native zoom capability (#684) 2023-04-03 08:24:56 -07:00
Nick Berardi
a2121c0dc5 alexa: add setting to publish debug events to console (#685) 2023-04-03 08:24:31 -07:00
Koushik Dutta
9b5ea27c0b core: fix checkbox ui 2023-04-03 08:23:20 -07:00
Koushik Dutta
0b0e90fc04 server: fix version being off by 1 in release notes/tag 2023-04-02 10:13:52 -07:00
Koushik Dutta
d8aff609bf core: publish 2023-04-02 09:37:54 -07:00
Koushik Dutta
d8283c261a homekit: publish beta 2023-04-02 09:37:49 -07:00
Koushik Dutta
e3aca964be Merge branch 'main' of github.com:koush/scrypted 2023-04-02 09:34:07 -07:00
Koushik Dutta
a96025c45f prerelease 2023-04-02 09:33:56 -07:00
Koushik Dutta
6afd4b4579 server: aggressively kill python plugin processes and forks 2023-04-02 09:33:48 -07:00
Brett Jia
f97669949d sdk, core: add Charger interface (#680)
* add Charger interface

* add charger icon to web ui

* import correct path

* get charge state displayed correctly
2023-04-01 21:03:54 -07:00
Koushik Dutta
0a0a31574f Merge branch 'main' of github.com:koush/scrypted 2023-04-01 15:02:46 -07:00
Koushik Dutta
90fb751a22 reolink: stream spec hints 2023-04-01 15:02:39 -07:00
Brett Jia
b8d06fada5 arlo: toggle for wired power + audio sensors (#679)
* remove hints to force prebuffer snapshots to fetch stream

* cleanup exception guard + catch prebuffer snapshot errors

* settings to remove Battery interface

* delayed init to load battery percentage

* fix plugin crash due to missing smart features dict

* properly add toggle for wired power

* fix race condition when multiple settings are updated at once

* bump 0.7.10 for beta

* audio detection + clean up battery/no-battery settings

* bump 0.7.11 for beta

* remove basestation models from camera class

* bump 0.7.12 for release
2023-04-01 14:28:54 -07:00
Koushik Dutta
2cecb1686f core: fix ui hang, readd launcher 2023-03-31 23:21:10 -07:00
Koushik Dutta
db03775530 prerelease 2023-03-31 20:37:25 -07:00
Koushik Dutta
cccbc33f1a server: detect 32/64 mixed mode issue and provide hint on how to fix. https://github.com/koush/scrypted/issues/678 2023-03-31 20:37:14 -07:00
Koushik Dutta
5f23873366 videoanalysis: fix bug where motion sensor would stop on invalid condition 2023-03-31 12:37:52 -07:00
Koushik Dutta
e43accae67 Merge branch 'main' of github.com:koush/scrypted 2023-03-31 09:45:02 -07:00
Koushik Dutta
b3a0cda6f9 python-codecs: fix vips/yuv/gray fast path 2023-03-31 09:44:57 -07:00
Alex Leeds
58c3348282 hap: merge in sirens as child devices (#674)
* hap: merge in sirens as child devices

* add subtype to onOff base
2023-03-31 07:44:38 -07:00
Koushik Dutta
a9e6d76e99 python-codecs: fix libav jpeg export 2023-03-30 23:59:05 -07:00
Koushik Dutta
3b58936387 predict: remove dead code 2023-03-30 09:35:47 -07:00
Koushik Dutta
3a14ab81c8 sample: update 2023-03-30 09:35:37 -07:00
Koushik Dutta
291178a7b5 sdk/client: update 2023-03-30 09:34:57 -07:00
Koushik Dutta
b65faf1a79 opencv: add gray toBuffer fast path 2023-03-30 09:34:45 -07:00
Koushik Dutta
9d8a1353c0 opencv: fix motion box translation 2023-03-29 17:25:24 -07:00
Koushik Dutta
b29d793178 ring: remove accidental clearing of clips cache 2023-03-29 16:42:09 -07:00
Koushik Dutta
d8e406d415 webrtc: reduce debug logging 2023-03-29 16:41:16 -07:00
Koushik Dutta
4529872fd6 videoanalysis: make sharp optional 2023-03-29 14:03:35 -07:00
Koushik Dutta
fa86c31340 prerelease 2023-03-29 12:41:56 -07:00
Koushik Dutta
94ded75d40 docker: fix watchtower token 2023-03-29 12:17:05 -07:00
Koushik Dutta
887b61cd7a prebeta 2023-03-29 11:58:54 -07:00
Koushik Dutta
48e3d30987 server: output docker flavor to logs 2023-03-29 11:58:43 -07:00
Koushik Dutta
02dba3cd71 docker: include flavor in env variable 2023-03-29 11:57:11 -07:00
Koushik Dutta
195769034d docker: include flavor in env variable 2023-03-29 11:56:50 -07:00
Koushik Dutta
39c08aa378 prebeta 2023-03-29 10:19:18 -07:00
Koushik Dutta
fa8056d38e python: purge packages on update 2023-03-29 10:18:34 -07:00
Koushik Dutta
145f116c68 webrtc/h264: reset stapa sent flag after every idr frame 2023-03-29 09:37:41 -07:00
Koushik Dutta
15b6f336e4 common: add h264 fragment information parsing 2023-03-29 08:18:13 -07:00
Koushik Dutta
8b46f0a466 openv: use new pipieline 2023-03-29 08:17:52 -07:00
Koushik Dutta
a20cc5cd89 docker: always install packages for arm 2023-03-29 08:01:08 -07:00
Koushik Dutta
3d068929fd predict: publish 2023-03-28 19:40:14 -07:00
137 changed files with 1350 additions and 3542 deletions

View File

@@ -250,7 +250,8 @@ export class BrowserSignalingSession implements RTCSignalingSession {
function logSendCandidate(console: Console, type: string, session: RTCSignalingSession): RTCSignalingSendIceCandidate {
return async (candidate) => {
try {
console.log(`${type} trickled candidate:`, candidate.sdpMLineIndex, candidate.candidate);
if (localStorage.getItem('debugLog') === 'true')
console.log(`${type} trickled candidate:`, candidate.sdpMLineIndex, candidate.candidate);
await session.addIceCandidate(candidate);
}
catch (e) {
@@ -297,7 +298,7 @@ export async function connectRTCSignalingClients(
if (offerOptions?.offer && answerOptions?.offer)
throw new Error('Both RTC clients have offers and can not negotiate. Consider implementing this in @scrypted/webrtc.');
if (offerOptions?.requiresOffer && answerOptions.requiresOffer)
if (offerOptions?.requiresOffer && answerOptions?.requiresOffer)
throw new Error('Both RTC clients require offers and can not negotiate.');
offerSetup.type = 'offer';
@@ -308,11 +309,13 @@ export async function connectRTCSignalingClients(
const offer = await offerClient.createLocalDescription('offer', offerSetup as RTCAVSignalingSetup,
disableTrickle ? undefined : answerQueue.queueSendCandidate);
console.log('offer sdp', offer.sdp);
if (localStorage.getItem('debugLog') === 'true')
console.log('offer sdp', offer.sdp);
await answerClient.setRemoteDescription(offer, answerSetup as RTCAVSignalingSetup);
const answer = await answerClient.createLocalDescription('answer', answerSetup as RTCAVSignalingSetup,
disableTrickle ? undefined : offerQueue.queueSendCandidate);
console.log('answer sdp', answer.sdp);
if (localStorage.getItem('debugLog') === 'true')
console.log('answer sdp', answer.sdp);
await offerClient.setRemoteDescription(answer, offerSetup as RTCAVSignalingSetup);
offerQueue.flush();
answerQueue.flush();

View File

@@ -129,6 +129,16 @@ export function getNaluTypes(streamChunk: StreamChunk) {
return getNaluTypesInNalu(streamChunk.chunks[streamChunk.chunks.length - 1].subarray(12))
}
export function getNaluFragmentInformation(nalu: Buffer) {
const naluType = nalu[0] & 0x1f;
const fua = naluType === H264_NAL_TYPE_FU_A;
return {
fua,
fuaStart: fua && !!(nalu[1] & 0x80),
fuaEnd: fua && !!(nalu[1] & 0x40),
}
}
export function getNaluTypesInNalu(nalu: Buffer, fuaRequireStart = false, fuaRequireEnd = false) {
const ret = new Set<number>();
const naluType = nalu[0] & 0x1f;

View File

@@ -63,7 +63,7 @@ RUN apt-get -y install \
# which causes weird behavior in python which looks at the arch version
# which still reports 64bit, even if running in 32bit docker.
# this scenario is not supported and will be reported at runtime.
RUN if [ "$(uname -m)" = "armv7l" ]; \
RUN if [ "$(uname -m)" != "x86_64" ]; \
then \
apt-get -y install \
python3-matplotlib \
@@ -95,7 +95,8 @@ ENV SCRYPTED_INSTALL_PATH="/server"
# changing this forces pip and npm to perform reinstalls.
# if this base image changes, this version must be updated.
ENV SCRYPTED_BASE_VERSION=20230322
ENV SCRYPTED_BASE_VERSION=20230329
ENV SCRYPTED_DOCKER_FLAVOR=full
################################################################
# End section generated from template/Dockerfile.full.footer

View File

@@ -42,4 +42,5 @@ ENV SCRYPTED_INSTALL_PATH="/server"
# changing this forces pip and npm to perform reinstalls.
# if this base image changes, this version must be updated.
ENV SCRYPTED_BASE_VERSION=20230322
ENV SCRYPTED_BASE_VERSION=20230329
ENV SCRYPTED_DOCKER_FLAVOR=lite

View File

@@ -21,4 +21,5 @@ ENV SCRYPTED_INSTALL_PATH="/server"
# changing this forces pip and npm to perform reinstalls.
# if this base image changes, this version must be updated.
ENV SCRYPTED_BASE_VERSION=20230322
ENV SCRYPTED_BASE_VERSION=20230329
ENV SCRYPTED_DOCKER_FLAVOR=thin

View File

@@ -42,7 +42,7 @@ fi
WATCHTOWER_HTTP_API_TOKEN=$(echo $RANDOM | md5sum)
DOCKER_COMPOSE_YML=$SCRYPTED_HOME/docker-compose.yml
echo "Created $DOCKER_COMPOSE_YML"
curl -s https://raw.githubusercontent.com/koush/scrypted/main/docker/docker-compose.yml | sed s/SET_THIS_TO_SOME_RANDOM_TEXT/"$(echo $RANDOM | md5sum)"/g > $DOCKER_COMPOSE_YML
curl -s https://raw.githubusercontent.com/koush/scrypted/main/docker/docker-compose.yml | sed s/SET_THIS_TO_SOME_RANDOM_TEXT/"$(echo $RANDOM | md5sum | head -c 32)"/g > $DOCKER_COMPOSE_YML
echo "Setting permissions on $SCRYPTED_HOME"
chown -R $SERVICE_USER $SCRYPTED_HOME

View File

@@ -10,7 +10,8 @@ ENV SCRYPTED_INSTALL_PATH="/server"
# changing this forces pip and npm to perform reinstalls.
# if this base image changes, this version must be updated.
ENV SCRYPTED_BASE_VERSION=20230322
ENV SCRYPTED_BASE_VERSION=20230329
ENV SCRYPTED_DOCKER_FLAVOR=full
################################################################
# End section generated from template/Dockerfile.full.footer

View File

@@ -60,7 +60,7 @@ RUN apt-get -y install \
# which causes weird behavior in python which looks at the arch version
# which still reports 64bit, even if running in 32bit docker.
# this scenario is not supported and will be reported at runtime.
RUN if [ "$(uname -m)" = "armv7l" ]; \
RUN if [ "$(uname -m)" != "x86_64" ]; \
then \
apt-get -y install \
python3-matplotlib \

View File

@@ -27,13 +27,6 @@ echo "sdk > npm run build"
npm run build
popd
pushd external/HAP-NodeJS
echo "external/HAP-NodeJS > npm install"
npm install
echo "external/HAP-NodeJS > npm run build"
npm run build
popd
pushd external/werift
echo "external/werift > npm install"
npm install

View File

@@ -9,7 +9,7 @@
"version": "1.1.43",
"license": "ISC",
"dependencies": {
"@scrypted/types": "^0.2.76",
"@scrypted/types": "^0.2.78",
"axios": "^0.25.0",
"engine.io-client": "^6.4.0",
"rimraf": "^3.0.2"
@@ -21,9 +21,9 @@
}
},
"node_modules/@scrypted/types": {
"version": "0.2.76",
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.76.tgz",
"integrity": "sha512-/7n8ICkXj8TGba4cHvckLCgSNsOmOGQ8I+Jd8fX9sxkthgsZhF5At8PHhHdkCDS+yfSmfXHkcqluZZOfYPkpAg=="
"version": "0.2.78",
"resolved": "https://registry.npmjs.org/@scrypted/types/-/types-0.2.78.tgz",
"integrity": "sha512-SiIUh9ph96aZPjt/oO+W/mlJobrP02ADwFDI9jnvw8/UegUti2x/7JE8Pi3kGXOIkN+cX74Qg4xJEMIpdpO1zw=="
},
"node_modules/@socket.io/component-emitter": {
"version": "3.1.0",

View File

@@ -17,7 +17,7 @@
"typescript": "^4.9.5"
},
"dependencies": {
"@scrypted/types": "^0.2.76",
"@scrypted/types": "^0.2.78",
"axios": "^0.25.0",
"engine.io-client": "^6.4.0",
"rimraf": "^3.0.2"

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/alexa",
"version": "0.2.3",
"version": "0.2.4",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",
"prescrypted-setup-project": "scrypted-package-json",

View File

@@ -15,6 +15,11 @@ const includeToken = 4;
export let DEBUG = false;
function debug(...args: any[]) {
if (DEBUG)
console.debug(...args);
}
class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, MixinProvider, Settings {
storageSettings = new StorageSettings(this, {
tokenInfo: {
@@ -34,6 +39,14 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
description: 'This is the endpoint Alexa will use to send events to. This is set after you login.',
type: 'string',
readonly: true
},
debug: {
title: 'Debug Events',
description: 'Log all events to the console. This will be very noisy and should not be left enabled.',
type: 'boolean',
onPut(oldValue: boolean, newValue: boolean) {
DEBUG = newValue;
}
}
});
@@ -44,6 +57,8 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
constructor(nativeId?: string) {
super(nativeId);
DEBUG = this.storageSettings.values.debug ?? false;
alexaHandlers.set('Alexa.Authorization/AcceptGrant', this.onAlexaAuthorization);
alexaHandlers.set('Alexa.Discovery/Discover', this.onDiscoverEndpoints);
@@ -141,12 +156,23 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
if (!supportedType)
return;
const report = await supportedType.sendEvent(eventSource, eventDetails, eventData);
let report = await supportedType.sendEvent(eventSource, eventDetails, eventData);
if (!report && eventDetails.eventInterface === ScryptedInterface.Online) {
report = {};
}
if (!report && eventDetails.eventInterface === ScryptedInterface.Battery) {
report = {};
}
if (!report) {
this.console.warn(`${eventDetails.eventInterface}.${eventDetails.property} not supported for device ${eventSource.type}`);
return;
}
debug("event", eventDetails.eventInterface, eventDetails.property, eventSource.type);
let data = {
"event": {
"header": {
@@ -234,7 +260,7 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
const endpoint = await this.getAlexaEndpoint();
const self = this;
this.console.assert(!DEBUG, `event:`, data);
debug("send event to alexa", data);
return axios.post(`https://${endpoint}/v3/events`, data, {
headers: {
@@ -570,6 +596,8 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
const { authorization } = request.headers;
if (!this.validAuths.has(authorization)) {
try {
debug("making authorization request to Scrypted");
await axios.get('https://home.scrypted.app/_punch/getcookie', {
headers: {
'Authorization': authorization,
@@ -590,11 +618,11 @@ class AlexaPlugin extends ScryptedDeviceBase implements HttpRequestHandler, Mixi
const { directive } = body;
const { namespace, name } = directive.header;
this.console.assert(!DEBUG, `request: ${namespace}/${name}`);
const mapName = `${namespace}/${name}`;
const handler = alexaHandlers.get(mapName);
debug("received directive from alexa", mapName, body);
const handler = alexaHandlers.get(mapName);
if (handler)
return handler.apply(this, [request, response, directive]);
@@ -641,7 +669,7 @@ class HttpResponseLoggingImpl implements AlexaHttpResponse {
if (options.code !== 200)
this.console.error(`response error ${options.code}:`, body);
else
this.console.assert(!DEBUG, `response ${options.code}:`, body);
debug("response to alexa directive", options.code, body);
if (typeof body === 'object')
body = JSON.stringify(body);

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/amcrest",
"version": "0.0.120",
"version": "0.0.121",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/amcrest",
"version": "0.0.120",
"version": "0.0.121",
"license": "Apache",
"dependencies": {
"@koush/axios-digest-auth": "^0.8.5",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/amcrest",
"version": "0.0.120",
"version": "0.0.121",
"description": "Amcrest Plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/arlo",
"version": "0.7.7",
"version": "0.7.12",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/arlo",
"version": "0.7.7",
"version": "0.7.12",
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/arlo",
"version": "0.7.7",
"version": "0.7.12",
"description": "Arlo Plugin for Scrypted",
"keywords": [
"scrypted",

View File

@@ -383,6 +383,33 @@ class Arlo(object):
self.HandleEvents(basestation, resource, [('is', 'motionDetected')], callbackwrapper)
)
def SubscribeToAudioEvents(self, basestation, camera, callback):
"""
Use this method to subscribe to audio events. You must provide a callback function which will get called once per audio event.
The callback function should have the following signature:
def callback(self, event)
This is an example of handling a specific event, in reality, you'd probably want to write a callback for HandleEvents()
that has a big switch statement in it to handle all the various events Arlo produces.
Returns the Task object that contains the subscription loop.
"""
resource = f"cameras/{camera.get('deviceId')}"
def callbackwrapper(self, event):
properties = event.get('properties', {})
stop = None
if 'audioDetected' in properties:
stop = callback(properties['audioDetected'])
if not stop:
return None
return stop
return asyncio.get_event_loop().create_task(
self.HandleEvents(basestation, resource, [('is', 'audioDetected')], callbackwrapper)
)
def SubscribeToBatteryEvents(self, basestation, camera, callback):
"""
Use this method to subscribe to battery events. You must provide a callback function which will get called once per battery event.
@@ -864,11 +891,11 @@ class Arlo(object):
}
)
def GetSmartFeatures(self, device):
def GetSmartFeatures(self, device) -> dict:
smart_features = self._getSmartFeaturesCached()
key = f"{device['owner']['ownerId']}_{device['deviceId']}"
return smart_features["features"].get(key)
return smart_features["features"].get(key, {})
@cached(cache=TTLCache(maxsize=1, ttl=60))
def _getSmartFeaturesCached(self):
def _getSmartFeaturesCached(self) -> dict:
return self.request.get(f'https://{self.BASE_URL}/hmsweb/users/subscription/smart/features')

View File

@@ -66,15 +66,4 @@ class ArloDeviceBase(ScryptedDeviceBase, ScryptedDeviceLoggerMixin, BackgroundTa
def get_builtin_child_device_manifests(self) -> List[Device]:
"""Returns the list of child device manifests representing hardware features built into this device."""
return []
@classmethod
def async_print_exception_guard(self, fn):
"""Decorator to print an exception's stack trace before re-raising the exception."""
async def wrapped(*args, **kwargs):
try:
return await fn(*args, **kwargs)
except Exception:
traceback.print_exc()
raise
return wrapped
return []

View File

@@ -10,20 +10,20 @@ from typing import List, TYPE_CHECKING
import scrypted_arlo_go
import scrypted_sdk
from scrypted_sdk.types import Setting, Settings, Device, Camera, VideoCamera, VideoClips, VideoClip, VideoClipOptions, MotionSensor, Battery, DeviceProvider, MediaObject, ResponsePictureOptions, ResponseMediaStreamOptions, ScryptedMimeTypes, ScryptedInterface, ScryptedDeviceType
from scrypted_sdk.types import Setting, Settings, Device, Camera, VideoCamera, VideoClips, VideoClip, VideoClipOptions, MotionSensor, AudioSensor, Battery, DeviceProvider, MediaObject, ResponsePictureOptions, ResponseMediaStreamOptions, ScryptedMimeTypes, ScryptedInterface, ScryptedDeviceType
from .base import ArloDeviceBase
from .spotlight import ArloSpotlight, ArloFloodlight
from .vss import ArloSirenVirtualSecuritySystem
from .child_process import HeartbeatChildProcess
from .util import BackgroundTaskMixin
from .util import BackgroundTaskMixin, async_print_exception_guard
if TYPE_CHECKING:
# https://adamj.eu/tech/2021/05/13/python-type-hints-how-to-fix-circular-imports/
from .provider import ArloProvider
class ArloCamera(ArloDeviceBase, Settings, Camera, VideoCamera, DeviceProvider, VideoClips, MotionSensor, Battery):
class ArloCamera(ArloDeviceBase, Settings, Camera, VideoCamera, DeviceProvider, VideoClips, MotionSensor, AudioSensor, Battery):
MODELS_WITH_SPOTLIGHTS = [
"vmc4040p",
"vmc2030",
@@ -38,10 +38,6 @@ class ArloCamera(ArloDeviceBase, Settings, Camera, VideoCamera, DeviceProvider,
MODELS_WITH_FLOODLIGHTS = ["fb1001"]
MODELS_WITH_SIRENS = [
"vmb4000",
"vmb4500",
"vmb4540",
"vmb5000",
"vmc4040p",
"fb1001",
"vmc2030",
@@ -56,6 +52,25 @@ class ArloCamera(ArloDeviceBase, Settings, Camera, VideoCamera, DeviceProvider,
"vmc4030p",
]
MODELS_WITH_AUDIO_SENSORS = [
"vmc4040p",
"fb1001",
"vmc4041p",
"vmc4050p",
"vmc5040",
"vmc3040",
"vmc3040s",
"vmc4030",
"vml4030",
"vmc4030p",
]
MODELS_WITHOUT_BATTERY = [
"avd1001",
"vmc3040",
"vmc3040s",
]
timeout: int = 30
intercom_session = None
light: ArloSpotlight = None
@@ -64,6 +79,7 @@ class ArloCamera(ArloDeviceBase, Settings, Camera, VideoCamera, DeviceProvider,
def __init__(self, nativeId: str, arlo_device: dict, arlo_basestation: dict, provider: ArloProvider) -> None:
super().__init__(nativeId=nativeId, arlo_device=arlo_device, arlo_basestation=arlo_basestation, provider=provider)
self.start_motion_subscription()
self.start_audio_subscription()
self.start_battery_subscription()
def start_motion_subscription(self) -> None:
@@ -75,7 +91,22 @@ class ArloCamera(ArloDeviceBase, Settings, Camera, VideoCamera, DeviceProvider,
self.provider.arlo.SubscribeToMotionEvents(self.arlo_basestation, self.arlo_device, callback)
)
def start_audio_subscription(self) -> None:
if not self.has_audio_sensor:
return
def callback(audioDetected):
self.audioDetected = audioDetected
return self.stop_subscriptions
self.register_task(
self.provider.arlo.SubscribeToAudioEvents(self.arlo_basestation, self.arlo_device, callback)
)
def start_battery_subscription(self) -> None:
if self.wired_to_power:
return
def callback(batteryLevel):
self.batteryLevel = batteryLevel
return self.stop_subscriptions
@@ -89,7 +120,6 @@ class ArloCamera(ArloDeviceBase, Settings, Camera, VideoCamera, DeviceProvider,
ScryptedInterface.VideoCamera.value,
ScryptedInterface.Camera.value,
ScryptedInterface.MotionSensor.value,
ScryptedInterface.Battery.value,
ScryptedInterface.Settings.value,
])
@@ -101,9 +131,18 @@ class ArloCamera(ArloDeviceBase, Settings, Camera, VideoCamera, DeviceProvider,
results.add(ScryptedInterface.RTCSignalingChannel.value)
results.discard(ScryptedInterface.Intercom.value)
if self.has_battery:
results.add(ScryptedInterface.Battery.value)
if self.wired_to_power:
results.discard(ScryptedInterface.Battery.value)
if self.has_siren or self.has_spotlight or self.has_floodlight:
results.add(ScryptedInterface.DeviceProvider.value)
if self.has_audio_sensor:
results.add(ScryptedInterface.AudioSensor.value)
if self.has_cloud_recording:
results.add(ScryptedInterface.VideoClips.value)
@@ -169,9 +208,16 @@ class ArloCamera(ArloDeviceBase, Settings, Camera, VideoCamera, DeviceProvider,
else:
return True
@property
def wired_to_power(self) -> bool:
if self.storage:
return True if self.storage.getItem("wired_to_power") else False
else:
return False
@property
def has_cloud_recording(self) -> bool:
return self.provider.arlo.GetSmartFeatures(self.arlo_device)["planFeatures"]["eventRecording"]
return self.provider.arlo.GetSmartFeatures(self.arlo_device).get("planFeatures", {}).get("eventRecording", False)
@property
def has_spotlight(self) -> bool:
@@ -185,9 +231,30 @@ class ArloCamera(ArloDeviceBase, Settings, Camera, VideoCamera, DeviceProvider,
def has_siren(self) -> bool:
return any([self.arlo_device["modelId"].lower().startswith(model) for model in ArloCamera.MODELS_WITH_SIRENS])
@property
def has_audio_sensor(self) -> bool:
return any([self.arlo_device["modelId"].lower().startswith(model) for model in ArloCamera.MODELS_WITH_AUDIO_SENSORS])
@property
def has_battery(self) -> bool:
return not any([self.arlo_device["modelId"].lower().startswith(model) for model in ArloCamera.MODELS_WITHOUT_BATTERY])
async def getSettings(self) -> List[Setting]:
result = []
if self.has_battery:
result.append(
{
"key": "wired_to_power",
"title": "Plugged In to External Power",
"value": self.wired_to_power,
"description": "Informs Scrypted that this device is plugged in to an external power source. " + \
"Will allow features like persistent prebuffer to work, however will no longer report this device's battery percentage. " + \
"Note that a persistent prebuffer may cause excess battery drain if the external power is not able to charge faster than the battery consumption rate.",
"type": "boolean",
},
)
if self._can_push_to_talk():
return [
result.extend([
{
"key": "two_way_audio",
"title": "(Experimental) Enable native two-way audio",
@@ -203,17 +270,18 @@ class ArloCamera(ArloDeviceBase, Settings, Camera, VideoCamera, DeviceProvider,
"If enabled, takes precedence over native two-way audio. May use increased system resources.",
"type": "boolean",
},
]
return []
])
return result
async def putSetting(self, key, value) -> None:
if key in ["webrtc_emulation", "two_way_audio"]:
if key in ["webrtc_emulation", "two_way_audio", "wired_to_power"]:
self.storage.setItem(key, value == "true")
await self.provider.discover_devices()
async def getPictureOptions(self) -> List[ResponsePictureOptions]:
return []
@async_print_exception_guard
async def takePicture(self, options: dict = None) -> MediaObject:
self.logger.info("Taking picture")
@@ -221,7 +289,11 @@ class ArloCamera(ArloDeviceBase, Settings, Camera, VideoCamera, DeviceProvider,
msos = await real_device.getVideoStreamOptions()
if any(["prebuffer" in m for m in msos]):
self.logger.info("Getting snapshot from prebuffer")
return await real_device.getVideoStream({"refresh": False})
try:
return await real_device.getVideoStream({"refresh": False})
except Exception as e:
self.logger.warning(f"Could not fetch from prebuffer due to: {e}")
self.logger.warning("Will try to fetch snapshot from Arlo cloud")
pic_url = await asyncio.wait_for(self.provider.arlo.TriggerFullFrameSnapshot(self.arlo_basestation, self.arlo_device), timeout=self.timeout)
self.logger.debug(f"Got snapshot URL for at {pic_url}")
@@ -273,32 +345,30 @@ class ArloCamera(ArloDeviceBase, Settings, Camera, VideoCamera, DeviceProvider,
}
return await scrypted_sdk.mediaManager.createFFmpegMediaObject(ffmpeg_input)
@async_print_exception_guard
async def startRTCSignalingSession(self, scrypted_session):
try:
plugin_session = ArloCameraRTCSignalingSession(self)
await plugin_session.initialize()
plugin_session = ArloCameraRTCSignalingSession(self)
await plugin_session.initialize()
scrypted_setup = {
"type": "offer",
"audio": {
"direction": "sendrecv" if self._can_push_to_talk() else "recvonly",
},
"video": {
"direction": "recvonly",
}
scrypted_setup = {
"type": "offer",
"audio": {
"direction": "sendrecv" if self._can_push_to_talk() else "recvonly",
},
"video": {
"direction": "recvonly",
}
plugin_setup = {}
}
plugin_setup = {}
scrypted_offer = await scrypted_session.createLocalDescription("offer", scrypted_setup, sendIceCandidate=plugin_session.addIceCandidate)
self.logger.info(f"Scrypted offer sdp:\n{scrypted_offer['sdp']}")
await plugin_session.setRemoteDescription(scrypted_offer, plugin_setup)
plugin_answer = await plugin_session.createLocalDescription("answer", plugin_setup, scrypted_session.sendIceCandidate)
self.logger.info(f"Scrypted answer sdp:\n{plugin_answer['sdp']}")
await scrypted_session.setRemoteDescription(plugin_answer, scrypted_setup)
scrypted_offer = await scrypted_session.createLocalDescription("offer", scrypted_setup, sendIceCandidate=plugin_session.addIceCandidate)
self.logger.info(f"Scrypted offer sdp:\n{scrypted_offer['sdp']}")
await plugin_session.setRemoteDescription(scrypted_offer, plugin_setup)
plugin_answer = await plugin_session.createLocalDescription("answer", plugin_setup, scrypted_session.sendIceCandidate)
self.logger.info(f"Scrypted answer sdp:\n{plugin_answer['sdp']}")
await scrypted_session.setRemoteDescription(plugin_answer, scrypted_setup)
return ArloCameraRTCSessionControl(plugin_session)
except Exception as e:
self.logger.error(e)
return ArloCameraRTCSessionControl(plugin_session)
async def startIntercom(self, media) -> None:
self.logger.info("Starting intercom")
@@ -374,7 +444,7 @@ class ArloCamera(ArloDeviceBase, Settings, Camera, VideoCamera, DeviceProvider,
clips.reverse()
return clips
@ArloDeviceBase.async_print_exception_guard
@async_print_exception_guard
async def removeVideoClips(self, videoClipIds: List[str]) -> None:
# Arlo does support deleting, but let's be safe and disable that
raise Exception("deleting Arlo video clips is not implemented by this plugin")

View File

@@ -31,8 +31,4 @@ class ArloDoorbell(ArloCamera, BinarySensor):
def get_applicable_interfaces(self) -> List[str]:
camera_interfaces = super().get_applicable_interfaces()
camera_interfaces.append(ScryptedInterface.BinarySensor.value)
model_id = self.arlo_device['modelId'].lower()
if model_id.startswith("avd1001"):
camera_interfaces.remove(ScryptedInterface.Battery.value)
return camera_interfaces

View File

@@ -16,7 +16,7 @@ from .arlo import Arlo
from .arlo.arlo_async import change_stream_class
from .arlo.logging import logger as arlo_lib_logger
from .logging import ScryptedDeviceLoggerMixin
from .util import BackgroundTaskMixin
from .util import BackgroundTaskMixin, async_print_exception_guard
from .camera import ArloCamera
from .doorbell import ArloDoorbell
from .basestation import ArloBasestation
@@ -30,6 +30,7 @@ class ArloProvider(ScryptedDeviceBase, Settings, DeviceProvider, ScryptedDeviceL
scrypted_devices = None
_arlo = None
_arlo_mfa_complete_auth = None
device_discovery_lock: asyncio.Lock = None
plugin_verbosity_choices = {
"Normal": logging.INFO,
@@ -50,6 +51,7 @@ class ArloProvider(ScryptedDeviceBase, Settings, DeviceProvider, ScryptedDeviceL
self.imap = None
self.imap_signal = None
self.imap_skip_emails = None
self.device_discovery_lock = asyncio.Lock()
self.propagate_verbosity()
self.propagate_transport()
@@ -193,9 +195,6 @@ class ArloProvider(ScryptedDeviceBase, Settings, DeviceProvider, ScryptedDeviceL
(self.arlo_basestations[camera["parentId"]], camera) for camera in self.arlo_cameras.values()
])
for nativeId in self.arlo_cameras.keys():
await self.getDevice(nativeId)
self.arlo.event_stream.set_refresh_interval(self.refresh_interval)
except requests.exceptions.HTTPError as e:
traceback.print_exc()
@@ -558,8 +557,12 @@ class ArloProvider(ScryptedDeviceBase, Settings, DeviceProvider, ScryptedDeviceL
return False
return True
@ArloDeviceBase.async_print_exception_guard
async def discover_devices(self, duration: int = 0) -> None:
@async_print_exception_guard
async def discover_devices(self) -> None:
async with self.device_discovery_lock:
return await self.discover_devices_impl()
async def discover_devices_impl(self) -> None:
if not self.arlo:
raise Exception("Arlo client not connected, cannot discover devices")
@@ -581,7 +584,7 @@ class ArloProvider(ScryptedDeviceBase, Settings, DeviceProvider, ScryptedDeviceL
continue
self.arlo_basestations[nativeId] = basestation
device = await self.getDevice(nativeId)
device = await self.getDevice_impl(nativeId)
scrypted_interfaces = device.get_applicable_interfaces()
manifest = device.get_device_manifest()
self.logger.debug(f"Interfaces for {nativeId} ({basestation['modelId']}): {scrypted_interfaces}")
@@ -620,7 +623,7 @@ class ArloProvider(ScryptedDeviceBase, Settings, DeviceProvider, ScryptedDeviceL
# own basestation
self.arlo_basestations[camera["deviceId"]] = camera
device: ArloDeviceBase = await self.getDevice(nativeId)
device = await self.getDevice_impl(nativeId)
scrypted_interfaces = device.get_applicable_interfaces()
manifest = device.get_device_manifest()
self.logger.debug(f"Interfaces for {nativeId} ({camera['modelId']}): {scrypted_interfaces}")
@@ -660,6 +663,10 @@ class ArloProvider(ScryptedDeviceBase, Settings, DeviceProvider, ScryptedDeviceL
})
async def getDevice(self, nativeId: str) -> ArloDeviceBase:
async with self.device_discovery_lock:
return await self.getDevice_impl(nativeId)
async def getDevice_impl(self, nativeId: str) -> ArloDeviceBase:
ret = self.scrypted_devices.get(nativeId, None)
if ret is None:
ret = self.create_device(nativeId)

View File

@@ -5,6 +5,7 @@ from typing import List, TYPE_CHECKING
from scrypted_sdk.types import OnOff, SecuritySystemMode, ScryptedInterface, ScryptedDeviceType
from .base import ArloDeviceBase
from .util import async_print_exception_guard
if TYPE_CHECKING:
# https://adamj.eu/tech/2021/05/13/python-type-hints-how-to-fix-circular-imports/
@@ -25,7 +26,7 @@ class ArloSiren(ArloDeviceBase, OnOff):
def get_device_type(self) -> str:
return ScryptedDeviceType.Siren.value
@ArloDeviceBase.async_print_exception_guard
@async_print_exception_guard
async def turnOn(self) -> None:
from .basestation import ArloBasestation
self.logger.info("Turning on")
@@ -56,7 +57,7 @@ class ArloSiren(ArloDeviceBase, OnOff):
"triggered": True,
}
@ArloDeviceBase.async_print_exception_guard
@async_print_exception_guard
async def turnOff(self) -> None:
from .basestation import ArloBasestation
self.logger.info("Turning off")

View File

@@ -5,6 +5,7 @@ from typing import List, TYPE_CHECKING
from scrypted_sdk.types import OnOff, ScryptedInterface, ScryptedDeviceType
from .base import ArloDeviceBase
from .util import async_print_exception_guard
if TYPE_CHECKING:
# https://adamj.eu/tech/2021/05/13/python-type-hints-how-to-fix-circular-imports/
@@ -25,13 +26,13 @@ class ArloSpotlight(ArloDeviceBase, OnOff):
def get_device_type(self) -> str:
return ScryptedDeviceType.Light.value
@ArloDeviceBase.async_print_exception_guard
@async_print_exception_guard
async def turnOn(self) -> None:
self.logger.info("Turning on")
self.provider.arlo.SpotlightOn(self.arlo_basestation, self.arlo_device)
self.on = True
@ArloDeviceBase.async_print_exception_guard
@async_print_exception_guard
async def turnOff(self) -> None:
self.logger.info("Turning off")
self.provider.arlo.SpotlightOff(self.arlo_basestation, self.arlo_device)
@@ -40,13 +41,13 @@ class ArloSpotlight(ArloDeviceBase, OnOff):
class ArloFloodlight(ArloSpotlight):
@ArloDeviceBase.async_print_exception_guard
@async_print_exception_guard
async def turnOn(self) -> None:
self.logger.info("Turning on")
self.provider.arlo.FloodlightOn(self.arlo_basestation, self.arlo_device)
self.on = True
@ArloDeviceBase.async_print_exception_guard
@async_print_exception_guard
async def turnOff(self) -> None:
self.logger.info("Turning off")
self.provider.arlo.FloodlightOff(self.arlo_basestation, self.arlo_device)

View File

@@ -1,4 +1,5 @@
import asyncio
import traceback
class BackgroundTaskMixin:
@@ -25,4 +26,14 @@ class BackgroundTaskMixin:
if not hasattr(self, "background_tasks"):
return
for task in self.background_tasks:
task.cancel()
task.cancel()
def async_print_exception_guard(fn):
"""Decorator to print an exception's stack trace before re-raising the exception."""
async def wrapped(*args, **kwargs):
try:
return await fn(*args, **kwargs)
except Exception:
traceback.print_exc()
raise
return wrapped

View File

@@ -7,6 +7,7 @@ from scrypted_sdk.types import Device, DeviceProvider, Setting, Settings, Settin
from .base import ArloDeviceBase
from .siren import ArloSiren
from .util import async_print_exception_guard
if TYPE_CHECKING:
# https://adamj.eu/tech/2021/05/13/python-type-hints-how-to-fix-circular-imports/
@@ -15,7 +16,7 @@ if TYPE_CHECKING:
from .camera import ArloCamera
class ArloSirenVirtualSecuritySystem(ArloDeviceBase, SecuritySystem, DeviceProvider):
class ArloSirenVirtualSecuritySystem(ArloDeviceBase, SecuritySystem, Settings, Readme, DeviceProvider):
"""A virtual, emulated security system that controls when scrypted events can trip the real physical siren."""
SUPPORTED_MODES = [SecuritySystemMode.AwayArmed.value, SecuritySystemMode.HomeArmed.value, SecuritySystemMode.Disarmed.value]
@@ -133,6 +134,7 @@ If this virtual security system is synced to Homekit, the siren device will be m
self.siren = ArloSiren(siren_id, self.arlo_device, self.arlo_basestation, self.provider, self)
return self.siren
@async_print_exception_guard
async def armSecuritySystem(self, mode: SecuritySystemMode) -> None:
self.logger.info(f"Arming {mode}")
self.mode = mode
@@ -143,7 +145,7 @@ If this virtual security system is synced to Homekit, the siren device will be m
if mode == SecuritySystemMode.Disarmed.value:
await self.get_or_create_siren().turnOff()
@ArloDeviceBase.async_print_exception_guard
@async_print_exception_guard
async def disarmSecuritySystem(self) -> None:
self.logger.info(f"Disarming")
self.mode = SecuritySystemMode.Disarmed.value

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/core",
"version": "0.1.103",
"version": "0.1.108",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/core",
"version": "0.1.103",
"version": "0.1.108",
"license": "Apache-2.0",
"dependencies": {
"@scrypted/common": "file:../../common",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/core",
"version": "0.1.103",
"version": "0.1.108",
"description": "Scrypted Core plugin. Provides the UI, websocket, and engine.io APIs.",
"author": "Scrypted",
"license": "Apache-2.0",

View File

@@ -92,6 +92,17 @@ class ScryptedCore extends ScryptedDeviceBase implements HttpRequestHandler, Eng
this.automationCore = new AutomationCore();
})();
deviceManager.onDeviceDiscovered({
name: 'Add to Launcher',
nativeId: 'launcher',
interfaces: [
'@scrypted/launcher-ignore',
ScryptedInterface.MixinProvider,
ScryptedInterface.Readme,
],
type: ScryptedDeviceType.Builtin,
});
(async () => {
await deviceManager.onDeviceDiscovered(
{

View File

@@ -23,6 +23,15 @@ export class User extends ScryptedDeviceBase implements Settings, ScryptedUser {
})
async getScryptedUserAccessControl(): Promise<ScryptedUserAccessControl> {
const usersService = await sdk.systemManager.getComponent('users');
const users: DBUser[] = await usersService.getAllUsers();
const user = users.find(user => user.username === this.username);
if (!user)
throw new Error("user not found");
if (user.admin)
return;
const self = sdk.deviceManager.getDeviceState(this.nativeId);
const ret: ScryptedUserAccessControl = {

View File

@@ -1,3 +1,4 @@
import { timeoutPromise } from "@scrypted/common/src/promise-utils";
import { MixinProvider, ScryptedDevice, ScryptedDeviceType, ScryptedInterface, SystemManager } from "@scrypted/types";
export async function setMixin(systemManager: SystemManager, device: ScryptedDevice, mixinId: string, enabled: boolean) {
@@ -14,19 +15,21 @@ export async function setMixin(systemManager: SystemManager, device: ScryptedDev
plugins.setMixins(device.id, mixins);
}
export function getAllDevices(systemManager: SystemManager) {
return Object.keys(systemManager.getSystemState()).map(id => systemManager.getDeviceById(id)).filter(device => !!device);
export function getAllDevices<T>(systemManager: SystemManager) {
return Object.keys(systemManager.getSystemState()).map(id => systemManager.getDeviceById(id) as T & ScryptedDevice).filter(device => !!device);
}
export async function getDeviceAvailableMixins(systemManager: SystemManager, device: ScryptedDevice): Promise<(ScryptedDevice & MixinProvider)[]> {
const results = await Promise.all(getAllDevices(systemManager).map(async (check) => {
const results = await Promise.all(getAllDevices<MixinProvider>(systemManager).map(async (check) => {
try {
if (check.interfaces.includes(ScryptedInterface.MixinProvider)) {
if (await (check as any as MixinProvider).canMixin(device.type, device.interfaces))
return check as MixinProvider & ScryptedDevice;
const canMixin = await timeoutPromise(5000, check.canMixin(device.type, device.interfaces));
if (canMixin)
return check;
}
}
catch (e) {
console.warn(check.name, 'canMixin error', e)
}
}));
@@ -47,7 +50,7 @@ export async function getMixinProviderAvailableDevices(systemManager: SystemMana
devices.map(async (device) => {
try {
if (device.mixins?.includes(mixinProvider.id) || (await mixinProvider.canMixin(device.type, device.interfaces)))
return device;
return device;
}
catch (e) {
}

View File

@@ -215,6 +215,7 @@ import Notifier from "../interfaces/Notifier.vue";
import OnOff from "../interfaces/OnOff.vue";
import Brightness from "../interfaces/Brightness.vue";
import Battery from "../interfaces/Battery.vue";
import Charger from "../interfaces/Charger.vue";
import Lock from "../interfaces/Lock.vue";
import ColorSettingHsv from "../interfaces/ColorSettingHsv.vue";
import ColorSettingRgb from "../interfaces/ColorSettingRgb.vue";
@@ -263,6 +264,7 @@ const cardHeaderInterfaces = [
ScryptedInterface.AudioSensor,
ScryptedInterface.HumiditySensor,
ScryptedInterface.Thermometer,
ScryptedInterface.Charger,
ScryptedInterface.Battery,
ScryptedInterface.Lock,
ScryptedInterface.OnOff,
@@ -362,6 +364,7 @@ export default {
Lock,
OnOff,
Charger,
Battery,
Thermometer,
HumiditySensor,

View File

@@ -0,0 +1,52 @@
<template>
<v-tooltip left>
<template v-slot:activator="{ on }">
<v-icon
v-on="on"
v-if="lazyValue.chargeState === Charging"
class="mr-1 mr-1"
small
>fa-plug</v-icon>
<v-icon
v-on="on"
v-else-if="lazyValue.chargeState == Trickle"
class="mr-1 mr-1"
small
>fa-plug-circle-minus</v-icon>
<v-icon
v-on="on"
v-else
class="mr-1 mr-1"
small
>fa-plug-circle-xmark</v-icon>
</template>
<span>{{ chargeText }}</span>
</v-tooltip>
</template>
<script>
import { ChargeState } from '@scrypted/types';
import RPCInterface from "./RPCInterface.vue";
export default {
mixins: [RPCInterface],
data() {
return {
Charging: ChargeState.Charging,
Trickle: ChargeState.Trickle,
NotCharging: ChargeState.NotCharging,
};
},
computed: {
chargeText() {
if (this.lazyValue.chargeState === "trickle") {
return "Trickle Charging";
}
if (this.lazyValue.chargeState === "charging") {
return "Charging";
}
return "Not Charging";
},
},
};
</script>

View File

@@ -81,6 +81,7 @@ export default {
const mediaManager = this.$scrypted.mediaManager;
const mo = await mediaManager.createMediaObject(buffer, 'image/*');
const detected = await this.rpc().detectObjects(mo);
console.log(detected);
this.lastDetection = detected;
},
allowDrop(ev) {

View File

@@ -41,8 +41,7 @@
</template>
</DevicePicker>
<DevicePicker v-else-if="lazyValue.type === 'interface'" v-model="lazyValue.value" :multiple="lazyValue.multiple"
:readonly="lazyValue.readonly" :devices="interfaces" :title="lazyValue.title"
:description="lazyValue.description">
:readonly="lazyValue.readonly" :devices="interfaces" :title="lazyValue.title" :description="lazyValue.description">
<template v-slot:append-outer>
<v-btn v-if="dirty && device" color="success" @click="save" class="shift-up">
<v-icon>send</v-icon>
@@ -52,7 +51,7 @@
<div v-else-if="lazyValue.type === 'clippath'" class="mb-2">
<v-btn small block @click="editZone">{{ lazyValue.title }} </v-btn>
<Camera :value="device" :device="device" :clipPathValue="sanitizedClipPathValue" :showDialog="editingZone"
:hidePreview="true" @dialog="editingZoneChanged" @clipPath="lazyValue.value = $event"></Camera>
:hidePreview="true" @dialog="editingZoneChanged" @clipPath="updateClipPath"></Camera>
</div>
<v-textarea v-else-if="lazyValue.type === 'textarea'" v-model="lazyValue.value" outlined persistent-hint
:hint="lazyValue.description" :label="lazyValue.title">
@@ -88,6 +87,7 @@ export default {
data() {
return {
editingZone: false,
clipPathThrottle: null,
};
},
watch: {
@@ -142,7 +142,7 @@ export default {
);
},
set(val) {
this.lazyValue.value = val.toString();
this.lazyValue.value = !!val;
},
},
dirty() {
@@ -228,6 +228,17 @@ export default {
},
methods: {
onChange() { },
updateClipPath(e) {
clearTimeout(this.clipPathThrottle);
this.clipPathThrottle = setTimeout(() => {
this.lazyValue.value = e;
this.rpc().putSetting(
this.lazyValue.key,
this.createInputValue().value
);
this.onInput();
}, 500)
},
editingZoneChanged(value) {
this.editingZone = value;
if (!value) {

View File

@@ -58,6 +58,8 @@ import {
faLightbulb,
faToggleOn,
faPlug,
faPlugCircleMinus,
faPlugCircleXmark,
faExclamationTriangle,
faSun,
faCode,
@@ -150,6 +152,8 @@ const icons: IconDefinition[] =[
faLightbulb,
faToggleOn,
faPlug,
faPlugCircleMinus,
faPlugCircleXmark,
faExclamationTriangle,
faSun,
faCode,

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/coreml",
"version": "0.1.5",
"version": "0.1.9",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/coreml",
"version": "0.1.5",
"version": "0.1.9",
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
}

View File

@@ -41,5 +41,5 @@
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
},
"version": "0.1.5"
"version": "0.1.9"
}

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/ffmpeg-camera",
"version": "0.0.20",
"version": "0.0.21",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/ffmpeg-camera",
"version": "0.0.20",
"version": "0.0.21",
"license": "Apache",
"dependencies": {
"@koush/axios-digest-auth": "^0.8.5",
@@ -36,7 +36,7 @@
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.2.68",
"version": "0.2.86",
"dev": true,
"license": "ISC",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/ffmpeg-camera",
"version": "0.0.20",
"version": "0.0.21",
"description": "FFmpeg Camera Plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",

View File

@@ -144,7 +144,7 @@ export abstract class CameraBase<T extends ResponseMediaStreamOptions> extends S
if (key === 'defaultStream') {
const vsos = await this.getVideoStreamOptions();
const stream = vsos.find(vso => vso.name === value);
this.storage.setItem('defaultStream', stream?.id);
this.storage.setItem('defaultStream', stream?.id || '');
}
else {
this.storage.setItem(key, value.toString());

View File

@@ -1,13 +1,12 @@
{
"name": "@scrypted/gstreamer-camera",
"version": "0.0.3",
"version": "0.0.5",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/gstreamer-camera",
"version": "0.0.3",
"hasInstallScript": true,
"version": "0.0.5",
"license": "Apache",
"dependencies": {
"@koush/axios-digest-auth": "^0.8.5",
@@ -37,39 +36,40 @@
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.0.199",
"version": "0.2.86",
"dev": true,
"license": "ISC",
"dependencies": {
"@babel/preset-typescript": "^7.16.7",
"@babel/preset-typescript": "^7.18.6",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"babel-loader": "^8.2.3",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.13.8",
"esbuild": "^0.15.9",
"ncp": "^2.0.0",
"raw-loader": "^4.0.2",
"rimraf": "^3.0.2",
"tmp": "^0.2.1",
"webpack": "^5.59.0"
"ts-loader": "^9.4.2",
"typescript": "^4.9.4",
"webpack": "^5.75.0",
"webpack-bundle-analyzer": "^4.5.0"
},
"bin": {
"scrypted-changelog": "bin/scrypted-changelog.js",
"scrypted-debug": "bin/scrypted-debug.js",
"scrypted-deploy": "bin/scrypted-deploy.js",
"scrypted-deploy-debug": "bin/scrypted-deploy-debug.js",
"scrypted-package-json": "bin/scrypted-package-json.js",
"scrypted-readme": "bin/scrypted-readme.js",
"scrypted-setup-project": "bin/scrypted-setup-project.js",
"scrypted-webpack": "bin/scrypted-webpack.js"
},
"devDependencies": {
"@types/node": "^16.11.1",
"@types/node": "^18.11.18",
"@types/stringify-object": "^4.0.0",
"stringify-object": "^3.3.0",
"ts-node": "^10.4.0",
"typedoc": "^0.22.8",
"typescript-json-schema": "^0.50.1",
"webpack-bundle-analyzer": "^4.5.0"
"typedoc": "^0.23.21"
}
},
"../sdk": {
@@ -141,9 +141,9 @@
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"node_modules/url-parse": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
"integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"dependencies": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
@@ -174,23 +174,24 @@
"@scrypted/sdk": {
"version": "file:../../sdk",
"requires": {
"@babel/preset-typescript": "^7.16.7",
"@types/node": "^16.11.1",
"@babel/preset-typescript": "^7.18.6",
"@types/node": "^18.11.18",
"@types/stringify-object": "^4.0.0",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"babel-loader": "^8.2.3",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.13.8",
"esbuild": "^0.15.9",
"ncp": "^2.0.0",
"raw-loader": "^4.0.2",
"rimraf": "^3.0.2",
"stringify-object": "^3.3.0",
"tmp": "^0.2.1",
"ts-loader": "^9.4.2",
"ts-node": "^10.4.0",
"typedoc": "^0.22.8",
"typescript-json-schema": "^0.50.1",
"webpack": "^5.59.0",
"typedoc": "^0.23.21",
"typescript": "^4.9.4",
"webpack": "^5.75.0",
"webpack-bundle-analyzer": "^4.5.0"
}
},
@@ -229,9 +230,9 @@
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"url-parse": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz",
"integrity": "sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ==",
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"requires": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/gstreamer-camera",
"version": "0.0.3",
"version": "0.0.5",
"description": "GStreamer Camera Plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",

View File

@@ -1,4 +1,4 @@
import sdk, { ScryptedDeviceBase, DeviceProvider, Settings, Setting, ScryptedDeviceType, VideoCamera, MediaObject, MediaStreamOptions, ScryptedInterface, FFmpegInput, Camera, PictureOptions, SettingValue, DeviceCreator, DeviceCreatorSettings } from "@scrypted/sdk";
import sdk, { ScryptedDeviceBase, DeviceProvider, Settings, Setting, ScryptedDeviceType, VideoCamera, MediaObject, MediaStreamOptions, ScryptedInterface, FFmpegInput, Camera, PictureOptions, SettingValue, DeviceCreator, DeviceCreatorSettings, ResponseMediaStreamOptions } from "@scrypted/sdk";
import { recommendRebroadcast } from "./recommend";
import AxiosDigestAuth from '@koush/axios-digest-auth';
import https from 'https';
@@ -14,7 +14,7 @@ export interface UrlMediaStreamOptions extends MediaStreamOptions {
url: string;
}
export abstract class CameraBase<T extends MediaStreamOptions> extends ScryptedDeviceBase implements Camera, VideoCamera, Settings {
export abstract class CameraBase<T extends ResponseMediaStreamOptions> extends ScryptedDeviceBase implements Camera, VideoCamera, Settings {
snapshotAuth: AxiosDigestAuth;
pendingPicture: Promise<MediaObject>;
@@ -194,7 +194,7 @@ export abstract class CameraBase<T extends MediaStreamOptions> extends ScryptedD
if (key === 'defaultStream') {
const vsos = await this.getVideoStreamOptions();
const stream = vsos.find(vso => vso.name === value);
this.storage.setItem('defaultStream', stream?.id);
this.storage.setItem('defaultStream', stream?.id || '');
}
else {
this.storage.setItem(key, value.toString());
@@ -220,7 +220,7 @@ export abstract class CameraBase<T extends MediaStreamOptions> extends ScryptedD
}
}
export abstract class CameraProviderBase<T extends MediaStreamOptions> extends ScryptedDeviceBase implements DeviceProvider, DeviceCreator {
export abstract class CameraProviderBase<T extends ResponseMediaStreamOptions> extends ScryptedDeviceBase implements DeviceProvider, DeviceCreator {
devices = new Map<string, any>();
constructor(nativeId?: string) {
@@ -234,6 +234,9 @@ export abstract class CameraProviderBase<T extends MediaStreamOptions> extends S
recommendRebroadcast();
}
async releaseDevice(id: string, nativeId: string): Promise<void> {
}
async createDevice(settings: DeviceCreatorSettings): Promise<string> {
const nativeId = randomBytes(4).toString('hex');
const name = settings.newCamera.toString();

View File

@@ -1,4 +1,4 @@
import sdk, { FFmpegInput, MediaObject, MediaStreamOptions, Setting, SettingValue } from "@scrypted/sdk";
import sdk, { FFmpegInput, MediaObject, MediaStreamOptions, ResponseMediaStreamOptions, Setting, SettingValue } from "@scrypted/sdk";
import child_process, { ChildProcess } from "child_process";
import { CameraProviderBase, CameraBase, UrlMediaStreamOptions } from "./common";
// import {} from "../../../common/src/stream-parser"
@@ -8,10 +8,10 @@ import { listenZero } from "../../../common/src/listen-cluster"
const { log, deviceManager, mediaManager } = sdk;
class GStreamerCamera extends CameraBase<MediaStreamOptions> {
class GStreamerCamera extends CameraBase<ResponseMediaStreamOptions> {
currentProcess: ChildProcess;
createGStreamerMediaStreamOptions(gstreamerInput: string, index: number): MediaStreamOptions {
createGStreamerMediaStreamOptions(gstreamerInput: string, index: number): ResponseMediaStreamOptions {
return {
id: `channel${index}`,
name: `Stream ${index + 1}`,
@@ -32,7 +32,7 @@ class GStreamerCamera extends CameraBase<MediaStreamOptions> {
return gstreamerInputs;
}
getRawVideoStreamOptions(): MediaStreamOptions[] {
getRawVideoStreamOptions(): ResponseMediaStreamOptions[] {
const gstreamerInputs = this.getGStreamerInputs();
// filter out empty strings.
@@ -86,7 +86,7 @@ class GStreamerCamera extends CameraBase<MediaStreamOptions> {
];
}
async createVideoStream(options?: MediaStreamOptions): Promise<MediaObject> {
async createVideoStream(options?: ResponseMediaStreamOptions): Promise<MediaObject> {
const index = this.getRawVideoStreamOptions()?.findIndex(vso => vso.id === options.id);
const gstreamerInputs = this.getGStreamerInputs();
const gstreamerInput = gstreamerInputs[index];
@@ -147,7 +147,7 @@ class GStreamerCamera extends CameraBase<MediaStreamOptions> {
}
class GStreamerProvider extends CameraProviderBase<MediaStreamOptions> {
class GStreamerProvider extends CameraProviderBase<ResponseMediaStreamOptions> {
createCamera(nativeId: string): GStreamerCamera {
return new GStreamerCamera(nativeId, this);
}

View File

@@ -41,7 +41,7 @@ The Channel number is the hundreds digit and (sub-)stream is ones digit:
# Troubleshooting
## General
* Not receiving motion alerts in the device's Scrypted event log? Check all of the following: **(1)** device has a motion detection grid drawn and enabled, **(2)** user or group access permissions of account used for device **(3)** do not use self-signed certs for HTTPS on the device, and **(4)** `CGI` and `ISAPI` integration protocol/service on device is enabled.
* Not receiving motion alerts in the device's Scrypted event log? Check all of the following: **(1)** device has a motion detection grid drawn and enabled, **(2)** user or group access permissions of account used for device **(3)** do not use self-signed certs for HTTPS on the device, **(4)** `CGI` and `ISAPI` integration protocol/service on device is enabled, and **(5)** that the authentication method on the device is set to "digest".
* If device has HTTPS enabled, try disabling HTTPS on the device to see if that resolves issue (do not use self-signed certs).
* If device has enabled user lockout, max connections, concurrent requests, etc., try disabling and/or increasing to max allowed for troubleshooting.
* Does your account (`Username`) have proper user and/or group permissions? Try granting all permissions for testing.

View File

@@ -1,27 +1,21 @@
{
"name": "@scrypted/hikvision",
"version": "0.0.124",
"version": "0.0.126",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/hikvision",
"version": "0.0.124",
"version": "0.0.126",
"license": "Apache",
"dependencies": {
"@koush/axios-digest-auth": "^0.8.5",
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",
"@types/highland": "^2.12.14",
"@types/lodash": "^4.14.172",
"@types/multiparty": "^0.0.33",
"@types/node": "^16.9.1",
"@types/xml2js": "^0.4.9",
"axios": "^0.23.0",
"highland": "^2.13.5",
"lodash": "^4.17.21",
"multiparty": "^4.2.2",
"net-keepalive": "^3.0.0",
"xml2js": "^0.4.23"
}
},
@@ -42,7 +36,7 @@
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.2.68",
"version": "0.2.86",
"license": "ISC",
"dependencies": {
"@babel/preset-typescript": "^7.18.6",
@@ -105,27 +99,6 @@
"resolved": "../../sdk",
"link": true
},
"node_modules/@types/highland": {
"version": "2.12.14",
"resolved": "https://registry.npmjs.org/@types/highland/-/highland-2.12.14.tgz",
"integrity": "sha512-afgFIPeRlysJjWAVmtxqt1nfRo29fjXwooX/MEc+GVlXKMiSsFOryY8hma1PNnjNjOI01Qe37/z5n3WGBk5WCg==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/lodash": {
"version": "4.14.172",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.172.tgz",
"integrity": "sha512-/BHF5HAx3em7/KkzVKm3LrsD6HZAXuXO1AJZQ3cRRBZj4oHZDviWPYu0aEplAqDFNHZPW6d3G7KN+ONcCCC7pw=="
},
"node_modules/@types/multiparty": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/@types/multiparty/-/multiparty-0.0.33.tgz",
"integrity": "sha512-Il6cJUpSqgojT7NxbVJUvXkCblm50/yEJYtblISDsNIeNYf4yMAhdizzidUk6h8pJ8yhwK/3Fkb+3Dwcgtwl8w==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "16.9.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
@@ -152,60 +125,6 @@
"follow-redirects": "^1.14.4"
}
},
"node_modules/debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ffi-napi": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/ffi-napi/-/ffi-napi-4.0.3.tgz",
"integrity": "sha512-PMdLCIvDY9mS32RxZ0XGb95sonPRal8aqRhLbeEtWKZTe2A87qRFG9HjOhvG8EX2UmQw5XNRMIOT+1MYlWmdeg==",
"hasInstallScript": true,
"dependencies": {
"debug": "^4.1.1",
"get-uv-event-loop-napi-h": "^1.0.5",
"node-addon-api": "^3.0.0",
"node-gyp-build": "^4.2.1",
"ref-napi": "^2.0.1 || ^3.0.2",
"ref-struct-di": "^1.1.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/ffi-napi/node_modules/debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/ffi-napi/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/follow-redirects": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
@@ -225,210 +144,16 @@
}
}
},
"node_modules/get-symbol-from-current-process-h": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.2.tgz",
"integrity": "sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw=="
},
"node_modules/get-uv-event-loop-napi-h": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/get-uv-event-loop-napi-h/-/get-uv-event-loop-napi-h-1.0.6.tgz",
"integrity": "sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg==",
"dependencies": {
"get-symbol-from-current-process-h": "^1.0.1"
}
},
"node_modules/highland": {
"version": "2.13.5",
"resolved": "https://registry.npmjs.org/highland/-/highland-2.13.5.tgz",
"integrity": "sha512-dn2flPapIIAa4BtkB2ahjshg8iSJtrJtdhEb9/oiOrS5HMQTR/GuhFpqJ+11YBdtnl3AwWKvbZd1Uxr8uAmA7A==",
"dependencies": {
"util-deprecate": "^1.0.2"
}
},
"node_modules/http-errors": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz",
"integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==",
"dependencies": {
"depd": "~1.1.2",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
},
"engines": {
"node": ">= 0.6"
}
},
"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/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node_modules/multiparty": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.2.2.tgz",
"integrity": "sha512-NtZLjlvsjcoGrzojtwQwn/Tm90aWJ6XXtPppYF4WmOk/6ncdwMMKggFY2NlRRN9yiCEIVxpOfPWahVEG2HAG8Q==",
"dependencies": {
"http-errors": "~1.8.0",
"safe-buffer": "5.2.1",
"uid-safe": "2.1.5"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/net-keepalive": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/net-keepalive/-/net-keepalive-3.0.0.tgz",
"integrity": "sha512-wfDa7VPeSltY5aIQcujS7AiWnO2JHJCpO3is4nwQ7kFYs4YMpzDNMwiuILPkWwgMbPMSHzO7O1tuL8rC0SP3ag==",
"dependencies": {
"ffi-napi": "^4.0.1",
"ref-napi": "^3.0.0"
},
"engines": {
"node": ">=10.20.0"
}
},
"node_modules/node-addon-api": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="
},
"node_modules/node-gyp-build": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz",
"integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==",
"bin": {
"node-gyp-build": "bin.js",
"node-gyp-build-optional": "optional.js",
"node-gyp-build-test": "build-test.js"
}
},
"node_modules/random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/ref-napi": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/ref-napi/-/ref-napi-3.0.3.tgz",
"integrity": "sha512-LiMq/XDGcgodTYOMppikEtJelWsKQERbLQsYm0IOOnzhwE9xYZC7x8txNnFC9wJNOkPferQI4vD4ZkC0mDyrOA==",
"hasInstallScript": true,
"dependencies": {
"debug": "^4.1.1",
"get-symbol-from-current-process-h": "^1.0.2",
"node-addon-api": "^3.0.0",
"node-gyp-build": "^4.2.1"
},
"engines": {
"node": ">= 10.0"
}
},
"node_modules/ref-napi/node_modules/debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/ref-napi/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/ref-struct-di": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ref-struct-di/-/ref-struct-di-1.1.1.tgz",
"integrity": "sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g==",
"dependencies": {
"debug": "^3.1.0"
}
},
"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/sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"node_modules/statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
"engines": {
"node": ">=0.6"
}
},
"node_modules/uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"dependencies": {
"random-bytes": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"node_modules/xml2js": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
@@ -505,27 +230,6 @@
"webpack-bundle-analyzer": "^4.5.0"
}
},
"@types/highland": {
"version": "2.12.14",
"resolved": "https://registry.npmjs.org/@types/highland/-/highland-2.12.14.tgz",
"integrity": "sha512-afgFIPeRlysJjWAVmtxqt1nfRo29fjXwooX/MEc+GVlXKMiSsFOryY8hma1PNnjNjOI01Qe37/z5n3WGBk5WCg==",
"requires": {
"@types/node": "*"
}
},
"@types/lodash": {
"version": "4.14.172",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.172.tgz",
"integrity": "sha512-/BHF5HAx3em7/KkzVKm3LrsD6HZAXuXO1AJZQ3cRRBZj4oHZDviWPYu0aEplAqDFNHZPW6d3G7KN+ONcCCC7pw=="
},
"@types/multiparty": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/@types/multiparty/-/multiparty-0.0.33.tgz",
"integrity": "sha512-Il6cJUpSqgojT7NxbVJUvXkCblm50/yEJYtblISDsNIeNYf4yMAhdizzidUk6h8pJ8yhwK/3Fkb+3Dwcgtwl8w==",
"requires": {
"@types/node": "*"
}
},
"@types/node": {
"version": "16.9.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz",
@@ -552,206 +256,21 @@
"follow-redirects": "^1.14.4"
}
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"ffi-napi": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/ffi-napi/-/ffi-napi-4.0.3.tgz",
"integrity": "sha512-PMdLCIvDY9mS32RxZ0XGb95sonPRal8aqRhLbeEtWKZTe2A87qRFG9HjOhvG8EX2UmQw5XNRMIOT+1MYlWmdeg==",
"requires": {
"debug": "^4.1.1",
"get-uv-event-loop-napi-h": "^1.0.5",
"node-addon-api": "^3.0.0",
"node-gyp-build": "^4.2.1",
"ref-napi": "^2.0.1 || ^3.0.2",
"ref-struct-di": "^1.1.0"
},
"dependencies": {
"debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"requires": {
"ms": "2.1.2"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"follow-redirects": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA=="
},
"get-symbol-from-current-process-h": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/get-symbol-from-current-process-h/-/get-symbol-from-current-process-h-1.0.2.tgz",
"integrity": "sha512-syloC6fsCt62ELLrr1VKBM1ggOpMdetX9hTrdW77UQdcApPHLmf7CI7OKcN1c9kYuNxKcDe4iJ4FY9sX3aw2xw=="
},
"get-uv-event-loop-napi-h": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/get-uv-event-loop-napi-h/-/get-uv-event-loop-napi-h-1.0.6.tgz",
"integrity": "sha512-t5c9VNR84nRoF+eLiz6wFrEp1SE2Acg0wS+Ysa2zF0eROes+LzOfuTaVHxGy8AbS8rq7FHEJzjnCZo1BupwdJg==",
"requires": {
"get-symbol-from-current-process-h": "^1.0.1"
}
},
"highland": {
"version": "2.13.5",
"resolved": "https://registry.npmjs.org/highland/-/highland-2.13.5.tgz",
"integrity": "sha512-dn2flPapIIAa4BtkB2ahjshg8iSJtrJtdhEb9/oiOrS5HMQTR/GuhFpqJ+11YBdtnl3AwWKvbZd1Uxr8uAmA7A==",
"requires": {
"util-deprecate": "^1.0.2"
}
},
"http-errors": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz",
"integrity": "sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": ">= 1.5.0 < 2",
"toidentifier": "1.0.0"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"multiparty": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/multiparty/-/multiparty-4.2.2.tgz",
"integrity": "sha512-NtZLjlvsjcoGrzojtwQwn/Tm90aWJ6XXtPppYF4WmOk/6ncdwMMKggFY2NlRRN9yiCEIVxpOfPWahVEG2HAG8Q==",
"requires": {
"http-errors": "~1.8.0",
"safe-buffer": "5.2.1",
"uid-safe": "2.1.5"
}
},
"net-keepalive": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/net-keepalive/-/net-keepalive-3.0.0.tgz",
"integrity": "sha512-wfDa7VPeSltY5aIQcujS7AiWnO2JHJCpO3is4nwQ7kFYs4YMpzDNMwiuILPkWwgMbPMSHzO7O1tuL8rC0SP3ag==",
"requires": {
"ffi-napi": "^4.0.1",
"ref-napi": "^3.0.0"
}
},
"node-addon-api": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
"integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="
},
"node-gyp-build": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz",
"integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q=="
},
"random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
},
"ref-napi": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/ref-napi/-/ref-napi-3.0.3.tgz",
"integrity": "sha512-LiMq/XDGcgodTYOMppikEtJelWsKQERbLQsYm0IOOnzhwE9xYZC7x8txNnFC9wJNOkPferQI4vD4ZkC0mDyrOA==",
"requires": {
"debug": "^4.1.1",
"get-symbol-from-current-process-h": "^1.0.2",
"node-addon-api": "^3.0.0",
"node-gyp-build": "^4.2.1"
},
"dependencies": {
"debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"requires": {
"ms": "2.1.2"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"ref-struct-di": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ref-struct-di/-/ref-struct-di-1.1.1.tgz",
"integrity": "sha512-2Xyn/0Qgz89VT+++WP0sTosdm9oeowLP23wRJYhG4BFdMUrLj3jhwHZNEytYNYgtPKLNTP3KJX4HEgBvM1/Y2g==",
"requires": {
"debug": "^3.1.0"
}
},
"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=="
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
},
"uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"requires": {
"random-bytes": "~1.0.0"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"xml2js": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/hikvision",
"version": "0.0.124",
"version": "0.0.126",
"description": "Hikvision Plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",

View File

@@ -1,25 +1,25 @@
{
"name": "@scrypted/homekit",
"version": "1.2.21",
"version": "1.2.23",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/homekit",
"version": "1.2.21",
"version": "1.2.23",
"dependencies": {
"@koush/werift-src": "file:../../external/werift",
"check-disk-space": "^3.3.1",
"hap-nodejs": "^0.11.0",
"lodash": "^4.17.21",
"mkdirp": "^2.1.5"
"mkdirp": "^2.1.6"
},
"devDependencies": {
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",
"@types/debug": "^4.1.7",
"@types/lodash": "^4.14.191",
"@types/node": "^18.15.5",
"@types/lodash": "^4.14.192",
"@types/node": "^18.15.11",
"@types/url-parse": "^1.4.8"
}
},
@@ -126,7 +126,7 @@
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.2.85",
"version": "0.2.86",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -276,9 +276,9 @@
}
},
"node_modules/@types/lodash": {
"version": "4.14.191",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
"version": "4.14.192",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.192.tgz",
"integrity": "sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==",
"dev": true
},
"node_modules/@types/ms": {
@@ -288,9 +288,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "18.15.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.5.tgz",
"integrity": "sha512-Ark2WDjjZO7GmvsyFFf81MXuGTA/d6oP38anyxWOL6EREyBKAxKoFHwBhaZxCfLRLpO8JgVXwqOwSwa7jRcjew==",
"version": "18.15.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
"dev": true
},
"node_modules/@types/url-parse": {
@@ -856,9 +856,9 @@
}
},
"node_modules/mkdirp": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.5.tgz",
"integrity": "sha512-jbjfql+shJtAPrFoKxHOXip4xS+kul9W3OzfzzrqueWK2QMGon2bFH2opl6W9EagBThjEz+iysyi/swOoVfB/w==",
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz",
"integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==",
"bin": {
"mkdirp": "dist/cjs/src/bin.js"
},
@@ -1276,9 +1276,9 @@
}
},
"@types/lodash": {
"version": "4.14.191",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
"version": "4.14.192",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.192.tgz",
"integrity": "sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==",
"dev": true
},
"@types/ms": {
@@ -1288,9 +1288,9 @@
"dev": true
},
"@types/node": {
"version": "18.15.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.5.tgz",
"integrity": "sha512-Ark2WDjjZO7GmvsyFFf81MXuGTA/d6oP38anyxWOL6EREyBKAxKoFHwBhaZxCfLRLpO8JgVXwqOwSwa7jRcjew==",
"version": "18.15.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
"dev": true
},
"@types/url-parse": {
@@ -1698,9 +1698,9 @@
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
},
"mkdirp": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.5.tgz",
"integrity": "sha512-jbjfql+shJtAPrFoKxHOXip4xS+kul9W3OzfzzrqueWK2QMGon2bFH2opl6W9EagBThjEz+iysyi/swOoVfB/w=="
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz",
"integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A=="
},
"ms": {
"version": "2.1.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/homekit",
"version": "1.2.21",
"version": "1.2.23",
"description": "HomeKit Plugin for Scrypted",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",
@@ -38,14 +38,14 @@
"check-disk-space": "^3.3.1",
"hap-nodejs": "^0.11.0",
"lodash": "^4.17.21",
"mkdirp": "^2.1.5"
"mkdirp": "^2.1.6"
},
"devDependencies": {
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",
"@types/debug": "^4.1.7",
"@types/lodash": "^4.14.191",
"@types/node": "^18.15.5",
"@types/lodash": "^4.14.192",
"@types/node": "^18.15.11",
"@types/url-parse": "^1.4.8"
}
}

View File

@@ -192,7 +192,7 @@ The latest troubleshooting guide for all known streaming or recording issues can
this.storage.setItem(key, JSON.stringify(value));
}
else {
this.storage.setItem(key, value?.toString());
this.storage.setItem(key, value?.toString() || '');
}
if (key === 'detectAudio' || key === 'linkedMotionSensor' || key === 'objectDetectionContactSensors') {

View File

@@ -303,13 +303,19 @@ addSupportedType({
}
}
// if the camera is a device provider, merge in child devices and
// ensure the devices are skipped by the rest of homekit by
// reporting that they've been merged
if (device.interfaces.includes(ScryptedInterface.DeviceProvider)) {
// merge in lights
const { devices } = mergeOnOffDevicesByType(device as ScryptedDevice as ScryptedDevice & DeviceProvider, accessory, ScryptedDeviceType.Light);
mergeOnOffDevicesByType(device as ScryptedDevice as ScryptedDevice & DeviceProvider, accessory, ScryptedDeviceType.Light).devices.forEach(device => {
homekitPlugin.mergedDevices.add(device.id)
});
// ensure child devices are skipped by the rest of homekit by
// reporting that they've been merged
devices.map(device => homekitPlugin.mergedDevices.add(device.id));
// merge in sirens
mergeOnOffDevicesByType(device as ScryptedDevice as ScryptedDevice & DeviceProvider, accessory, ScryptedDeviceType.Siren).devices.forEach(device => {
homekitPlugin.mergedDevices.add(device.id)
});
}
return accessory;

View File

@@ -64,6 +64,9 @@ export class H264Repacketizer {
extraPackets = 0;
fuaMax: number;
pendingFuA: RtpPacket[];
// log whether a stapa sps/pps has been seen.
// resets on every idr frame, to trigger codec information
// to be resent.
seenStapASps = false;
fuaMin: number;
@@ -402,8 +405,12 @@ export class H264Repacketizer {
// if this is an idr frame, but no sps has been sent via a stapa, dummy one up.
// the stream may not contain codec information in stapa or may be sending it
// in separate sps/pps packets which is not supported by homekit.
if (originalNalType === NAL_TYPE_IDR && !this.seenStapASps)
this.maybeSendSpsPps(packet, ret);
if (originalNalType === NAL_TYPE_IDR) {
if (!this.seenStapASps)
this.maybeSendSpsPps(packet, ret);
this.seenStapASps = false;
}
}
else {
if (this.pendingFuA) {
@@ -486,10 +493,12 @@ export class H264Repacketizer {
return;
}
if (nalType === NAL_TYPE_IDR && !this.seenStapASps) {
if (nalType === NAL_TYPE_IDR) {
// if this is an idr frame, but no sps has been sent, dummy one up.
// the stream may not contain sps.
this.maybeSendSpsPps(packet, ret);
if (!this.seenStapASps)
this.maybeSendSpsPps(packet, ret);
this.seenStapASps = false;
}
this.fragment(packet, ret);

View File

@@ -9,7 +9,7 @@ export function probe(device: DummyDevice): boolean {
}
export function getService(device: ScryptedDevice & OnOff, accessory: Accessory, serviceType: any): Service {
const service = accessory.addService(serviceType, device.name);
const service = accessory.addService(serviceType, device.name, device.nativeId);
service.getCharacteristic(Characteristic.On)
.on(CharacteristicEventTypes.SET, (value: CharacteristicValue, callback: CharacteristicSetCallback) => {
callback();

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/objectdetector",
"version": "0.0.119",
"version": "0.0.123",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/objectdetector",
"version": "0.0.119",
"version": "0.0.123",
"license": "Apache-2.0",
"dependencies": {
"@scrypted/common": "file:../../common",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/objectdetector",
"version": "0.0.119",
"version": "0.0.123",
"description": "Scrypted Video Analysis Plugin. Installed alongside a detection service like OpenCV or TensorFlow.",
"author": "Scrypted",
"license": "Apache-2.0",

View File

@@ -1,12 +1,32 @@
import { Deferred } from "@scrypted/common/src/deferred";
import { addVideoFilterArguments } from "@scrypted/common/src/ffmpeg-helpers";
import { ffmpegLogInitialOutput, safeKillFFmpeg, safePrintFFmpegArguments } from "@scrypted/common/src/media-helpers";
import { readLength, readLine } from "@scrypted/common/src/read-stream";
import { addVideoFilterArguments } from "@scrypted/common/src/ffmpeg-helpers";
import sdk, { FFmpegInput, Image, ImageOptions, MediaObject, ScryptedDeviceBase, ScryptedMimeTypes, VideoFrame, VideoFrameGenerator, VideoFrameGeneratorOptions } from "@scrypted/sdk";
import child_process from 'child_process';
import sharp from 'sharp';
import type sharp from 'sharp';
import { Readable } from 'stream';
export let sharpLib: (input?:
| Buffer
| Uint8Array
| Uint8ClampedArray
| Int8Array
| Uint16Array
| Int16Array
| Uint32Array
| Int32Array
| Float32Array
| Float64Array
| string,
options?: sharp.SharpOptions) => sharp.Sharp;
try {
sharpLib = require('sharp');
}
catch (e) {
console.warn('Sharp failed to load. FFmpeg Frame Generator will not function properly.')
}
async function createVipsMediaObject(image: VipsImage): Promise<VideoFrame & MediaObject> {
const ret = await sdk.mediaManager.createMediaObject(image, ScryptedMimeTypes.Image, {
format: null,
@@ -76,7 +96,20 @@ class VipsImage implements Image {
resolveWithObject: true,
});
const newImage = sharp(data, {
const sharpLib = require('sharp') as (input?:
| Buffer
| Uint8Array
| Uint8ClampedArray
| Int8Array
| Uint16Array
| Int16Array
| Uint32Array
| Int32Array
| Float32Array
| Float64Array
| string,
options?) => sharp.Sharp;
const newImage = sharpLib(data, {
raw: info,
});
@@ -177,7 +210,7 @@ export class FFmpegVideoFrameGenerator extends ScryptedDeviceBase implements Vid
const raw = await frameDeferred.promise;
const { width, height, data } = raw;
const image = sharp(data, {
const image = sharpLib(data, {
raw: {
width,
height,

View File

@@ -4,7 +4,7 @@ import crypto from 'crypto';
import { AutoenableMixinProvider } from "../../../common/src/autoenable-mixin-provider";
import { SettingsMixinDeviceBase } from "../../../common/src/settings-mixin";
import { DenoisedDetectionState } from './denoise';
import { FFmpegVideoFrameGenerator } from './ffmpeg-videoframes';
import { FFmpegVideoFrameGenerator, sharpLib } from './ffmpeg-videoframes';
import { serverSupportsMixinEventMasking } from './server-version';
import { sleep } from './sleep';
import { getAllDevices, safeParseJson } from './util';
@@ -61,6 +61,10 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
choices,
}
},
onPut: () => {
this.endObjectDetection();
this.maybeStartMotionDetection();
},
defaultValue: 'Default',
},
motionSensorSupplementation: {
@@ -201,7 +205,6 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
if (this.hasMotionType)
this.motionDetected = false;
this.detectorRunning = false;
this.endObjectDetection();
this.maybeStartMotionDetection();
@@ -324,6 +327,8 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
const start = Date.now();
let detections = 0;
const currentDetections = new Set<string>();
let lastReport = 0;
try {
for await (const detected
of await this.objectDetection.generateObjectDetections(await generator(), {
@@ -333,8 +338,7 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
if (!this.detectorRunning) {
break;
}
const now = Date.now();
if (now > this.analyzeStop) {
if (!this.hasMotionType && Date.now() > this.analyzeStop) {
break;
}
@@ -346,6 +350,22 @@ class ObjectDetectionMixin extends SettingsMixinDeviceBase<VideoCamera & Camera
detections++;
// this.console.warn('dps', detections / (Date.now() - start) * 1000);
if (!this.hasMotionType) {
for (const d of detected.detected.detections) {
currentDetections.add(d.className);
}
const now = Date.now();
if (now > lastReport + 3000) {
const found = [...currentDetections.values()];
if (!found.length)
found.push('[no detections]');
this.console.log(`[${Math.round((now - start) / 100) / 10}s] Detected:`, ...found);
currentDetections.clear();
lastReport = now;
}
}
if (detected.detected.detectionId) {
const jpeg = await detected.videoFrame.toBuffer({
format: 'jpg',
@@ -820,9 +840,9 @@ class ObjectDetectionPlugin extends AutoenableMixinProvider implements Settings,
{
name: 'FFmpeg Frame Generator',
type: ScryptedDeviceType.Builtin,
interfaces: [
interfaces: sharpLib ? [
ScryptedInterface.VideoFrameGenerator,
],
] : [],
nativeId: 'ffmpeg',
}
]

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/onvif",
"version": "0.0.118",
"version": "0.0.119",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/onvif",
"version": "0.0.118",
"version": "0.0.119",
"license": "Apache",
"dependencies": {
"@koush/axios-digest-auth": "^0.8.5",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/onvif",
"version": "0.0.118",
"version": "0.0.119",
"description": "ONVIF Camera Plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/opencv",
"version": "0.0.70",
"version": "0.0.74",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/opencv",
"version": "0.0.70",
"version": "0.0.74",
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
}

View File

@@ -36,5 +36,5 @@
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
},
"version": "0.0.70"
"version": "0.0.74"
}

View File

@@ -1,22 +1,46 @@
from __future__ import annotations
from time import sleep
from detect import DetectionSession, DetectPlugin
from typing import Any, List, Tuple
import numpy as np
import asyncio
import concurrent.futures
from typing import Any, List, Tuple
import cv2
import imutils
Gst = None
try:
from gi.repository import Gst
except:
pass
from scrypted_sdk.types import ObjectDetectionModel, ObjectDetectionResult, ObjectsDetected, Setting, VideoFrame
import numpy as np
import scrypted_sdk
from PIL import Image
from scrypted_sdk.types import (ObjectDetectionGeneratorSession,
ObjectDetectionResult, ObjectsDetected,
Setting, VideoFrame)
class OpenCVDetectionSession(DetectionSession):
from detect import DetectPlugin
# vips is already multithreaded, but needs to be kicked off the python asyncio thread.
toThreadExecutor = concurrent.futures.ThreadPoolExecutor(max_workers=2, thread_name_prefix="image")
async def to_thread(f):
loop = asyncio.get_running_loop()
return await loop.run_in_executor(toThreadExecutor, f)
async def ensureGrayData(data: bytes, size: Tuple[int, int], format: str):
if format == 'gray':
return data
def convert():
if format == 'rgba':
image = Image.frombuffer('RGBA', size, data)
else:
image = Image.frombuffer('RGB', size, data)
try:
return image.convert('L').tobytes()
finally:
image.close()
return await to_thread(convert)
class OpenCVDetectionSession:
def __init__(self) -> None:
super().__init__()
self.cap: cv2.VideoCapture = None
self.previous_frame: Any = None
self.curFrame = None
@@ -35,21 +59,6 @@ defaultBlur = 5
class OpenCVPlugin(DetectPlugin):
def __init__(self, nativeId: str | None = None):
super().__init__(nativeId=nativeId)
self.color2Gray = None
self.pixelFormat = "I420"
self.pixelFormatChannelCount = 1
if True:
self.retainAspectRatio = False
self.color2Gray = None
self.pixelFormat = "I420"
self.pixelFormatChannelCount = 1
else:
self.retainAspectRatio = True
self.color2Gray = cv2.COLOR_BGRA2GRAY
self.pixelFormat = "BGRA"
self.pixelFormatChannelCount = 4
def getClasses(self) -> list[str]:
return ['motion']
@@ -91,9 +100,6 @@ class OpenCVPlugin(DetectPlugin):
]
return settings
def get_pixel_format(self):
return self.pixelFormat
def get_input_format(self) -> str:
return 'gray'
@@ -110,17 +116,10 @@ class OpenCVPlugin(DetectPlugin):
blur = int(settings.get('blur', blur))
return area, threshold, interval, blur
def detect(self, detection_session: OpenCVDetectionSession, frame, src_size, convert_to_src_size) -> ObjectsDetected:
settings = detection_session.settings
def detect(self, frame, settings: Any, detection_session: OpenCVDetectionSession, src_size, convert_to_src_size) -> ObjectsDetected:
area, threshold, interval, blur = self.parse_settings(settings)
# see get_detection_input_size on undocumented size requirements for GRAY8
if self.color2Gray != None:
detection_session.gray = cv2.cvtColor(
frame, self.color2Gray, dst=detection_session.gray)
gray = detection_session.gray
else:
gray = frame
gray = frame
detection_session.curFrame = cv2.GaussianBlur(
gray, (blur, blur), 0, dst=detection_session.curFrame)
@@ -154,8 +153,8 @@ class OpenCVPlugin(DetectPlugin):
# if w * h != contour_area:
# print("mismatch w/h", contour_area - w * h)
x2, y2, _ = convert_to_src_size((x + w, y + h))
x, y, _ = convert_to_src_size((x, y))
x2, y2 = convert_to_src_size((x + w, y + h))
x, y = convert_to_src_size((x, y))
w = x2 - x + 1
h = y2 - y + 1
@@ -206,11 +205,24 @@ class OpenCVPlugin(DetectPlugin):
detection_session.cap = None
return super().end_session(detection_session)
async def run_detection_image(self, detection_session: DetectionSession, image: Image.Image, settings: Any, src_size, convert_to_src_size) -> Tuple[ObjectsDetected, Any]:
# todo
raise Exception('can not run motion detection on image')
async def run_detection_videoframe(self, videoFrame: VideoFrame, detection_session: OpenCVDetectionSession) -> ObjectsDetected:
async def generateObjectDetections(self, videoFrames: Any, session: ObjectDetectionGeneratorSession = None) -> Any:
try:
ds = OpenCVDetectionSession()
videoFrames = await scrypted_sdk.sdk.connectRPCObject(videoFrames)
async for videoFrame in videoFrames:
detected = await self.run_detection_videoframe(videoFrame, session and session.get('settings'), ds)
yield {
'__json_copy_serialize_children': True,
'detected': detected,
'videoFrame': videoFrame,
}
finally:
try:
await videoFrames.aclose()
except:
pass
async def run_detection_videoframe(self, videoFrame: VideoFrame, settings: Any, detection_session: OpenCVDetectionSession) -> ObjectsDetected:
width = videoFrame.width
height = videoFrame.height
@@ -234,64 +246,26 @@ class OpenCVPlugin(DetectPlugin):
'height': height,
}
format = videoFrame.format or 'gray'
buffer = await videoFrame.toBuffer({
'resize': resize,
'format': format,
})
def convert_to_src_size(point, normalize = False):
return point[0] * scale, point[1] * scale, True
mat = np.ndarray((height, width, self.pixelFormatChannelCount), buffer=buffer, dtype=np.uint8)
detections = self.detect(
detection_session, mat, (width, height), convert_to_src_size)
return detections
async def run_detection_avframe(self, detection_session: DetectionSession, avframe, settings: Any, src_size, convert_to_src_size) -> Tuple[ObjectsDetected, Any]:
if avframe.format.name != 'yuv420p' and avframe.format.name != 'yuvj420p':
mat = avframe.to_ndarray(format='gray8')
if format == 'gray':
expectedLength = width * height
# check if resize could not be completed
if expectedLength != len(buffer):
image = Image.frombuffer('L', (videoFrame.width, videoFrame.height), buffer)
try:
buffer = image.resize((width, height), Image.BILINEAR).tobytes()
finally:
image.close()
else:
mat = np.ndarray((avframe.height, avframe.width, self.pixelFormatChannelCount), buffer=avframe.planes[0], dtype=np.uint8)
detections = self.detect(
detection_session, mat, src_size, convert_to_src_size)
if not detections or not len(detections['detections']):
await self.detection_sleep(settings)
return None, None
return detections, None
buffer = await ensureGrayData(buffer, (width, height), format)
async def run_detection_gstsample(self, detection_session: OpenCVDetectionSession, gst_sample, settings: Any, src_size, convert_to_src_size) -> ObjectsDetected:
buf = gst_sample.get_buffer()
caps = gst_sample.get_caps()
# can't trust the width value, compute the stride
height = caps.get_structure(0).get_value('height')
width = caps.get_structure(0).get_value('width')
result, info = buf.map(Gst.MapFlags.READ)
if not result:
return None, None
try:
mat = np.ndarray(
(height,
width,
self.pixelFormatChannelCount),
buffer=info.data,
dtype=np.uint8)
detections = self.detect(
detection_session, mat, src_size, convert_to_src_size)
# no point in triggering empty events.
finally:
buf.unmap(info)
if not detections or not len(detections['detections']):
await self.detection_sleep(settings)
return None, None
return detections, None
def create_detection_session(self):
return OpenCVDetectionSession()
async def detection_sleep(self, settings: Any):
area, threshold, interval, blur = self.parse_settings(settings)
# it is safe to block here because gstreamer creates a queue thread
await asyncio.sleep(interval / 1000)
async def detection_event_notified(self, settings: Any):
await self.detection_sleep(settings)
return await super().detection_event_notified(settings)
def convert_to_src_size(point):
return point[0] * scale, point[1] * scale
mat = np.ndarray((height, width, 1), buffer=buffer, dtype=np.uint8)
detections = self.detect(mat, settings, detection_session, (videoFrame.width, videoFrame.height), convert_to_src_size)
return detections

View File

@@ -1 +0,0 @@
../../tensorflow-lite/src/pipeline

View File

@@ -3,10 +3,6 @@ numpy>=1.16.2
# pillow for anything not intel linux
Pillow>=5.4.1; sys_platform != 'linux' or platform_machine != 'x86_64'
pillow-simd; sys_platform == 'linux' and platform_machine == 'x86_64'
PyGObject>=3.30.4; sys_platform != 'win32'
imutils>=0.5.0
# not available on armhf
av>=10.0.0; sys_platform != 'linux' or platform_machine == 'x86_64' or platform_machine == 'aarch64'
# not available on armhf
opencv-python; sys_platform != 'linux' or platform_machine == 'x86_64' or platform_machine == 'aarch64'
opencv-python; sys_platform != 'linux' or platform_machine == 'x86_64'

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/pam-diff",
"version": "0.0.18",
"version": "0.0.20",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/pam-diff",
"version": "0.0.18",
"version": "0.0.20",
"hasInstallScript": true,
"dependencies": {
"@types/node": "^16.6.1",

View File

@@ -43,5 +43,5 @@
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
},
"version": "0.0.18"
"version": "0.0.20"
}

View File

@@ -9,7 +9,7 @@ import { PassThrough, Writable } from 'stream';
const { mediaManager } = sdk;
const defaultDifference = 9;
const defaultPercentage = 15;
const defaultPercentage = 2;
interface PamDiffSession {
id: string;

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/prebuffer-mixin",
"version": "0.9.79",
"version": "0.9.81",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/prebuffer-mixin",
"version": "0.9.79",
"version": "0.9.81",
"license": "Apache-2.0",
"dependencies": {
"@scrypted/common": "file:../../common",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/prebuffer-mixin",
"version": "0.9.79",
"version": "0.9.81",
"description": "Video Stream Rebroadcast, Prebuffer, and Management Plugin for Scrypted.",
"author": "Scrypted",
"license": "Apache-2.0",

View File

@@ -1618,7 +1618,7 @@ class PrebufferMixin extends SettingsMixinDeviceBase<VideoCamera> implements Vid
if (this.streamSettings.storageSettings.settings[key])
await this.streamSettings.storageSettings.putSetting(key, value);
else
this.storage.setItem(key, value?.toString());
this.storage.setItem(key, value?.toString() || '');
// no prebuffer change necessary if the setting is a transcoding hint.
if (this.streamSettings.storageSettings.settings[key]?.group === 'Transcoding')

View File

@@ -111,7 +111,7 @@ export function createStreamSettings(device: MixinDeviceBase<VideoCamera>) {
placeholder: '-hwaccel auto',
choices: Object.keys(getH264DecoderArgs()),
combobox: true,
mapPut: (oldValue, newValue) => getH264DecoderArgs()[newValue]?.join(' ') || newValue,
mapPut: (oldValue, newValue) => getH264DecoderArgs()[newValue]?.join(' ') || newValue || '',
hide: true,
},
videoFilterArguments: {

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/python-codecs",
"version": "0.1.25",
"version": "0.1.30",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@scrypted/python-codecs",
"version": "0.1.25",
"version": "0.1.30",
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/python-codecs",
"version": "0.1.25",
"version": "0.1.30",
"description": "Python Codecs for Scrypted",
"keywords": [
"scrypted",

View File

@@ -1,3 +1,4 @@
import traceback
import asyncio
import scrypted_sdk
from scrypted_sdk import Setting, SettingValue
@@ -6,6 +7,7 @@ import gstreamer
import libav
import vipsimage
import pilimage
import time
Gst = None
try:
@@ -128,19 +130,29 @@ def create_scrypted_plugin():
class CodecFork:
async def generateVideoFramesGstreamer(self, mediaObject: scrypted_sdk.MediaObject, options: scrypted_sdk.VideoFrameGeneratorOptions = None, filter: Any = None, h264Decoder: str = None) -> scrypted_sdk.VideoFrame:
start = time.time()
try:
async for data in gstreamer.generateVideoFramesGstreamer(mediaObject, options, filter, h264Decoder):
yield data
except Exception as e:
traceback.print_exc()
raise
finally:
print('gstreamer finished after %s' % (time.time() - start))
import os
os._exit(os.EX_OK)
pass
async def generateVideoFramesLibav(self, mediaObject: scrypted_sdk.MediaObject, options: scrypted_sdk.VideoFrameGeneratorOptions = None, filter: Any = None) -> scrypted_sdk.VideoFrame:
start = time.time()
try:
async for data in libav.generateVideoFramesLibav(mediaObject, options, filter):
yield data
except Exception as e:
traceback.print_exc()
raise
finally:
print('libav finished after %s' % (time.time() - start))
import os
os._exit(os.EX_OK)
pass

View File

@@ -37,7 +37,8 @@ class PILImage(scrypted_sdk.VideoFrame):
def save():
bytesArray = io.BytesIO()
pilImage.pilImage.save(bytesArray, format=options['format'])
pilImage.pilImage.save(bytesArray, format='JPEG')
# pilImage.pilImage.save(bytesArray, format=options['format'])
return bytesArray.getvalue()
return await to_thread(lambda: save())

View File

@@ -1,3 +1,6 @@
# needed by libav to_ndarray
numpy>=1.16.2
# gobject instrospection for gstreamer.
PyGObject>=3.30.4; sys_platform != 'win32'

View File

@@ -31,6 +31,10 @@ class VipsImage(scrypted_sdk.VideoFrame):
mem = memoryview(rgb.write_to_memory())
return mem
return await to_thread(format)
elif options['format'] == 'gray':
def format():
return memoryview(vipsImage.vipsImage.write_to_memory())
return await to_thread(format)
return await to_thread(lambda: vipsImage.vipsImage.write_to_buffer('.' + options['format']))

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/reolink",
"version": "0.0.17",
"version": "0.0.21",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/reolink",
"version": "0.0.17",
"version": "0.0.21",
"license": "Apache",
"dependencies": {
"@koush/axios-digest-auth": "^0.8.5",
@@ -36,38 +36,39 @@
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.0.206",
"version": "0.2.86",
"license": "ISC",
"dependencies": {
"@babel/preset-typescript": "^7.16.7",
"@babel/preset-typescript": "^7.18.6",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"babel-loader": "^8.2.3",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.13.8",
"esbuild": "^0.15.9",
"ncp": "^2.0.0",
"raw-loader": "^4.0.2",
"rimraf": "^3.0.2",
"tmp": "^0.2.1",
"webpack": "^5.59.0"
"ts-loader": "^9.4.2",
"typescript": "^4.9.4",
"webpack": "^5.75.0",
"webpack-bundle-analyzer": "^4.5.0"
},
"bin": {
"scrypted-changelog": "bin/scrypted-changelog.js",
"scrypted-debug": "bin/scrypted-debug.js",
"scrypted-deploy": "bin/scrypted-deploy.js",
"scrypted-deploy-debug": "bin/scrypted-deploy-debug.js",
"scrypted-package-json": "bin/scrypted-package-json.js",
"scrypted-readme": "bin/scrypted-readme.js",
"scrypted-setup-project": "bin/scrypted-setup-project.js",
"scrypted-webpack": "bin/scrypted-webpack.js"
},
"devDependencies": {
"@types/node": "^16.11.1",
"@types/node": "^18.11.18",
"@types/stringify-object": "^4.0.0",
"stringify-object": "^3.3.0",
"ts-node": "^10.4.0",
"typedoc": "^0.22.8",
"typescript-json-schema": "^0.50.1",
"webpack-bundle-analyzer": "^4.5.0"
"typedoc": "^0.23.21"
}
},
"../sdk": {
@@ -260,23 +261,24 @@
"@scrypted/sdk": {
"version": "file:../../sdk",
"requires": {
"@babel/preset-typescript": "^7.16.7",
"@types/node": "^16.11.1",
"@babel/preset-typescript": "^7.18.6",
"@types/node": "^18.11.18",
"@types/stringify-object": "^4.0.0",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"babel-loader": "^8.2.3",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.13.8",
"esbuild": "^0.15.9",
"ncp": "^2.0.0",
"raw-loader": "^4.0.2",
"rimraf": "^3.0.2",
"stringify-object": "^3.3.0",
"tmp": "^0.2.1",
"ts-loader": "^9.4.2",
"ts-node": "^10.4.0",
"typedoc": "^0.22.8",
"typescript-json-schema": "^0.50.1",
"webpack": "^5.59.0",
"typedoc": "^0.23.21",
"typescript": "^4.9.4",
"webpack": "^5.75.0",
"webpack-bundle-analyzer": "^4.5.0"
}
},

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/reolink",
"version": "0.0.17",
"version": "0.0.21",
"description": "Reolink Plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",

View File

@@ -115,6 +115,20 @@ class ReolinkCamera extends RtspSmartCamera implements Camera {
});
}
// rough guesses for rebroadcast stream selection.
ret[0].video = {
width: 2560,
height: 1920,
}
ret[1].video = {
width: 896,
height: 672,
}
ret[2].video = {
width: 640,
height: 480,
}
const channel = (this.getRtspChannel() + 1).toString().padStart(2, '0');
const rtspPreviews = [
`h264Preview_${channel}_main`,

View File

@@ -1,4 +1,4 @@
{
"scrypted.debugHost": "koushik-ubuntu",
"scrypted.debugHost": "127.0.0.1",
}

View File

@@ -1,18 +1,18 @@
{
"name": "@scrypted/ring",
"version": "0.0.106",
"version": "0.0.109",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@scrypted/ring",
"version": "0.0.106",
"version": "0.0.109",
"dependencies": {
"@koush/ring-client-api": "file:../../external/ring-client-api",
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",
"@types/node": "^18.15.3",
"axios": "^1.3.4",
"@types/node": "^18.15.11",
"axios": "^1.3.5",
"rxjs": "^7.8.0"
},
"optionalDependencies": {
@@ -42,14 +42,14 @@
],
"devDependencies": {
"@changesets/changelog-github": "^0.4.8",
"@changesets/cli": "^2.26.0",
"@changesets/cli": "^2.26.1",
"@swc-node/register": "^1.6.2",
"turbo": "^1.8.2"
"turbo": "^1.8.5"
}
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.2.85",
"version": "0.2.86",
"license": "ISC",
"dependencies": {
"@babel/preset-typescript": "^7.18.6",
@@ -148,9 +148,9 @@
}
},
"node_modules/@types/node": {
"version": "18.15.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz",
"integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw=="
"version": "18.15.11",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q=="
},
"node_modules/@types/responselike": {
"version": "1.0.0",
@@ -179,9 +179,9 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
"integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.5.tgz",
"integrity": "sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==",
"dependencies": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",

View File

@@ -36,13 +36,13 @@
"@koush/ring-client-api": "file:../../external/ring-client-api",
"@scrypted/common": "file:../../common",
"@scrypted/sdk": "file:../../sdk",
"@types/node": "^18.15.3",
"axios": "^1.3.4",
"@types/node": "^18.15.11",
"axios": "^1.3.5",
"rxjs": "^7.8.0"
},
"optionalDependencies": {
"got": "11.8.6",
"socket.io-client": "^2.5.0"
},
"version": "0.0.106"
"version": "0.0.109"
}

View File

@@ -702,7 +702,6 @@ export class RingCameraDevice extends ScryptedDeviceBase implements DeviceProvid
}
async getVideoClips(options?: VideoClipOptions): Promise<VideoClip[]> {
this.videoClips = new Map<string, VideoClip>;
const response = await this.camera.videoSearch({
dateFrom: options.startTime,
dateTo: options.endTime,

View File

@@ -2,6 +2,7 @@ import sdk, { Device, DeviceProvider, ScryptedDeviceBase, ScryptedDeviceType, Sc
import { StorageSettings } from '@scrypted/sdk/storage-settings';
import { RingLocationDevice } from './location';
import { generateUuid, Location, RingBaseApi, RingRestClient } from './ring-client-api';
import { sleep } from '@scrypted/common/src/sleep';
const { deviceManager, mediaManager } = sdk;
@@ -19,12 +20,18 @@ class RingPlugin extends ScryptedDeviceBase implements DeviceProvider, Settings
},
email: {
title: 'Email',
onPut: async () => this.clearTryDiscoverDevices(),
onPut: async () => {
if (await this.loginNextTick())
this.clearTryDiscoverDevices();
},
},
password: {
title: 'Password',
type: 'password',
onPut: async () => this.clearTryDiscoverDevices(),
onPut: async () => {
if (await this.loginNextTick())
this.clearTryDiscoverDevices();
},
},
loginCode: {
title: 'Two Factor Code',
@@ -72,8 +79,18 @@ class RingPlugin extends ScryptedDeviceBase implements DeviceProvider, Settings
this.settingsStorage.values.systemId = generateUuid();
}
waiting = false;
async loginNextTick() {
if (this.waiting)
return false;
this.waiting = true;
await sleep(500);
this.waiting = false;
return true;
}
async clearTryDiscoverDevices() {
this.settingsStorage.values.refreshToken = undefined;
this.settingsStorage.values.refreshToken = '';
await this.discoverDevices();
this.console.log('discovery completed successfully');
}
@@ -192,7 +209,7 @@ class RingPlugin extends ScryptedDeviceBase implements DeviceProvider, Settings
return this.devices.get(nativeId);
}
async releaseDevice(id: string, nativeId: string): Promise<void> {}
async releaseDevice(id: string, nativeId: string): Promise<void> { }
}
export default RingPlugin;

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/rtsp",
"version": "0.0.52",
"version": "0.0.53",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/rtsp",
"version": "0.0.52",
"version": "0.0.53",
"license": "Apache",
"dependencies": {
"@koush/axios-digest-auth": "^0.8.5",

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/rtsp",
"version": "0.0.52",
"version": "0.0.53",
"description": "RTSP Cameras and Streams Plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",

View File

@@ -327,7 +327,7 @@ export abstract class RtspSmartCamera extends RtspCamera {
}
setHttpPortOverride(port: string) {
this.storage.setItem('httpPort', port);
this.storage.setItem('httpPort', port || '');
}
getRtspUrlOverride() {

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/sip",
"version": "0.0.6",
"version": "0.0.8",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/sip",
"version": "0.0.6",
"version": "0.0.8",
"dependencies": {
"@homebridge/camera-utils": "^2.0.4",
"@slyoldfox/sip": "^0.0.6-1",
@@ -40,7 +40,7 @@
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.2.85",
"version": "0.2.86",
"dev": true,
"license": "ISC",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/sip",
"version": "0.0.6",
"version": "0.0.8",
"scripts": {
"scrypted-setup-project": "scrypted-setup-project",
"prescrypted-setup-project": "scrypted-package-json",

View File

@@ -59,7 +59,7 @@ class SipCamera extends ScryptedDeviceBase implements Intercom, Camera, VideoCam
else if (key === 'defaultStream') {
const vsos = await this.getVideoStreamOptions();
const stream = vsos.find(vso => vso.name === value);
this.storage.setItem('defaultStream', stream?.id);
this.storage.setItem('defaultStream', stream?.id || '');
}
else {
this.storage.setItem(key, value.toString());

View File

@@ -1,13 +1,12 @@
{
"name": "@scrypted/synology-ss",
"version": "0.0.14",
"version": "0.0.16",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/synology-ss",
"version": "0.0.14",
"hasInstallScript": true,
"version": "0.0.16",
"license": "Apache",
"dependencies": {
"axios": "^0.24.0"
@@ -19,39 +18,40 @@
},
"../../sdk": {
"name": "@scrypted/sdk",
"version": "0.0.199",
"version": "0.2.86",
"dev": true,
"license": "ISC",
"dependencies": {
"@babel/preset-typescript": "^7.16.7",
"@babel/preset-typescript": "^7.18.6",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"babel-loader": "^8.2.3",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.13.8",
"esbuild": "^0.15.9",
"ncp": "^2.0.0",
"raw-loader": "^4.0.2",
"rimraf": "^3.0.2",
"tmp": "^0.2.1",
"webpack": "^5.59.0"
"ts-loader": "^9.4.2",
"typescript": "^4.9.4",
"webpack": "^5.75.0",
"webpack-bundle-analyzer": "^4.5.0"
},
"bin": {
"scrypted-changelog": "bin/scrypted-changelog.js",
"scrypted-debug": "bin/scrypted-debug.js",
"scrypted-deploy": "bin/scrypted-deploy.js",
"scrypted-deploy-debug": "bin/scrypted-deploy-debug.js",
"scrypted-package-json": "bin/scrypted-package-json.js",
"scrypted-readme": "bin/scrypted-readme.js",
"scrypted-setup-project": "bin/scrypted-setup-project.js",
"scrypted-webpack": "bin/scrypted-webpack.js"
},
"devDependencies": {
"@types/node": "^16.11.1",
"@types/node": "^18.11.18",
"@types/stringify-object": "^4.0.0",
"stringify-object": "^3.3.0",
"ts-node": "^10.4.0",
"typedoc": "^0.22.8",
"typescript-json-schema": "^0.50.1",
"webpack-bundle-analyzer": "^4.5.0"
"typedoc": "^0.23.21"
}
},
"node_modules/@scrypted/sdk": {
@@ -73,9 +73,9 @@
}
},
"node_modules/follow-redirects": {
"version": "1.14.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==",
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"funding": [
{
"type": "individual",
@@ -96,23 +96,24 @@
"@scrypted/sdk": {
"version": "file:../../sdk",
"requires": {
"@babel/preset-typescript": "^7.16.7",
"@types/node": "^16.11.1",
"@babel/preset-typescript": "^7.18.6",
"@types/node": "^18.11.18",
"@types/stringify-object": "^4.0.0",
"adm-zip": "^0.4.13",
"axios": "^0.21.4",
"babel-loader": "^8.2.3",
"babel-loader": "^9.1.0",
"babel-plugin-const-enum": "^1.1.0",
"esbuild": "^0.13.8",
"esbuild": "^0.15.9",
"ncp": "^2.0.0",
"raw-loader": "^4.0.2",
"rimraf": "^3.0.2",
"stringify-object": "^3.3.0",
"tmp": "^0.2.1",
"ts-loader": "^9.4.2",
"ts-node": "^10.4.0",
"typedoc": "^0.22.8",
"typescript-json-schema": "^0.50.1",
"webpack": "^5.59.0",
"typedoc": "^0.23.21",
"typescript": "^4.9.4",
"webpack": "^5.75.0",
"webpack-bundle-analyzer": "^4.5.0"
}
},
@@ -131,9 +132,9 @@
}
},
"follow-redirects": {
"version": "1.14.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA=="
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@scrypted/synology-ss",
"version": "0.0.14",
"version": "0.0.16",
"description": "A Synology Surveillance Station plugin for Scrypted",
"author": "Scrypted",
"license": "Apache",

View File

@@ -41,7 +41,7 @@ class SynologyCameraDevice extends ScryptedDeviceBase implements Camera, HttpReq
}
public async putSetting(key: string, value: string | number | boolean) {
this.storage.setItem(key, value?.toString());
this.storage.setItem(key, value?.toString() || '');
this.onDeviceEvent(ScryptedInterface.Settings, undefined);
}
@@ -173,6 +173,9 @@ class SynologySurveillanceStation extends ScryptedDeviceBase implements Settings
this.startup = this.discoverDevices(0);
}
async releaseDevice(id: string, nativeId: string): Promise<void> {
}
public async discoverDevices(duration: number): Promise<void> {
const url = this.getSetting('url');
const username = this.getSetting('username');

View File

@@ -1,16 +1,16 @@
{
// docker installation
// "scrypted.debugHost": "koushik-windows",
// "scrypted.serverRoot": "/server",
"scrypted.debugHost": "koushik-ubuntu",
"scrypted.serverRoot": "/server",
// pi local installation
// "scrypted.debugHost": "192.168.2.119",
// "scrypted.serverRoot": "/home/pi/.scrypted",
// local checkout
"scrypted.debugHost": "127.0.0.1",
"scrypted.serverRoot": "/Users/koush/.scrypted",
// "scrypted.debugHost": "127.0.0.1",
// "scrypted.serverRoot": "/Users/koush/.scrypted",
// "scrypted.debugHost": "koushik-windows",
// "scrypted.serverRoot": "C:\\Users\\koush\\.scrypted",

View File

@@ -1,12 +1,12 @@
{
"name": "@scrypted/tensorflow-lite",
"version": "0.1.8",
"version": "0.1.9",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@scrypted/tensorflow-lite",
"version": "0.1.8",
"version": "0.1.9",
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
}

View File

@@ -44,5 +44,5 @@
"devDependencies": {
"@scrypted/sdk": "file:../../sdk"
},
"version": "0.1.8"
"version": "0.1.9"
}

Some files were not shown because too many files have changed in this diff Show More