Skip to content

Commit

Permalink
core: cleanup device discovery
Browse files Browse the repository at this point in the history
  • Loading branch information
koush committed Mar 7, 2023
1 parent ee22686 commit 83af0c5
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 23 deletions.
4 changes: 2 additions & 2 deletions plugins/core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion plugins/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@scrypted/core",
"version": "0.1.100",
"version": "0.1.101",
"description": "Scrypted Core plugin. Provides the UI, websocket, and engine.io APIs.",
"author": "Scrypted",
"license": "Apache-2.0",
Expand Down
55 changes: 39 additions & 16 deletions plugins/core/ui/src/interfaces/DeviceProvider.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,45 @@
Devices</v-btn>
</v-card-actions>

<v-simple-table v-if="discoveredDevices && discoveredDevices.length">
<thead>
<tr>
<th style="width: 10px;"></th>
<th>Discovered</th>
<th></th>
</tr>
</thead>
<tbody v-if="discoveredDevices.length">
<tr v-for="device in discoveredDevices" :key="device.id">
<td>
<v-btn x-small outlined fab color="info" @click="openDeviceAdoptionDialog(device)">
<v-icon>fa-solid
fa-plus</v-icon>
</v-btn>
</td>
<td>
{{ device.name }}
</td>
<td> {{ device.description }}</td>
</tr>
</tbody>
<tbody v-else>
<td></td>
<td>None found.</td>
<td></td>
</tbody>
</v-simple-table>


<v-card-text>These things were created by {{ device.name }}.</v-card-text>
<v-text-field v-model="search" append-icon="search" label="Search" single-line hide-details></v-text-field>
<v-data-table :headers="headers" :items="providerDevices.devices" :items-per-page="10" :search="search">
<v-text-field v-if="managedDevices.devices.length > 10" v-model="search" append-icon="search" label="Search"
single-line hide-details></v-text-field>
<v-data-table v-if="managedDevices.devices.length > 10" :headers="headers" :items="managedDevices.devices"
:items-per-page="10" :search="search">
<template v-slot:[`item.icon`]="{ item }">
<v-icon v-if="!item.nativeId" x-small color="grey">
<v-icon x-small color="grey">
{{ typeToIcon(item.type) }}
</v-icon>

<v-tooltip bottom v-else>
<template v-slot:activator="{ on }">
<v-btn x-small outlined fab v-on="on" color="info" @click="openDeviceAdoptionDialog(item)"><v-icon>fa-solid
fa-plus</v-icon></v-btn>
</template>
<span>Add Discovered Device</span>
</v-tooltip>

</template>
<template v-slot:[`item.name`]="{ item }">
<a v-if="!item.nativeId" link :href="'#' + getDeviceViewPath(item.id)">{{ item.name }}</a>
Expand All @@ -48,7 +71,7 @@
</template>
</v-data-table>

<!-- <DeviceGroup v-else :deviceGroup="providerDevices"></DeviceGroup> -->
<DeviceGroup v-else :deviceGroup="managedDevices"></DeviceGroup>
</v-flex>
</template>
<script>
Expand Down Expand Up @@ -167,8 +190,8 @@ export default {
});
return ret;
},
providerDevices() {
const currentDevices = this.$store.state.scrypted.devices
managedDevices() {
const devices = this.$store.state.scrypted.devices
.filter(
(id) =>
this.$store.state.systemState[id].providerId.value ===
Expand All @@ -181,7 +204,7 @@ export default {
}));
return {
devices: [...this.discoveredDevices || [], ...currentDevices],
devices,
};
},
},
Expand Down
71 changes: 67 additions & 4 deletions plugins/onvif/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import sdk, { AdoptDevice, Device, DeviceCreatorSettings, DeviceDiscovery, DeviceInformation, DiscoveredDevice, Intercom, MediaObject, MediaStreamOptions, ObjectDetectionTypes, ObjectDetector, ObjectsDetected, PictureOptions, ScryptedDeviceType, ScryptedInterface, ScryptedNativeId, Setting, Settings, SettingValue, VideoCamera, VideoCameraConfiguration } from "@scrypted/sdk";
import sdk, { AdoptDevice, Device, DeviceCreatorSettings, DeviceDiscovery, DeviceInformation, DiscoveredDevice, Intercom, MediaObject, MediaStreamOptions, ObjectDetectionTypes, ObjectDetector, ObjectsDetected, PanTiltZoom, PanTiltZoomCommand, PictureOptions, ScryptedDeviceType, ScryptedInterface, ScryptedNativeId, Setting, Settings, SettingValue, VideoCamera, VideoCameraConfiguration } from "@scrypted/sdk";
import { AddressInfo } from "net";
import onvif from 'onvif';
import { Stream } from "stream";
Expand Down Expand Up @@ -29,7 +29,7 @@ function convertAudioCodec(codec: string) {
return codec?.toLowerCase();
}

class OnvifCamera extends RtspSmartCamera implements ObjectDetector, Intercom, VideoCameraConfiguration {
class OnvifCamera extends RtspSmartCamera implements ObjectDetector, Intercom, VideoCameraConfiguration, PanTiltZoom {
eventStream: Stream;
client: OnvifCameraAPI;
rtspMediaStreamOptions: Promise<UrlMediaStreamOptions[]>;
Expand Down Expand Up @@ -338,6 +338,15 @@ class OnvifCamera extends RtspSmartCamera implements ObjectDetector, Intercom, V
return false;
}

getPtz() {
try {
return JSON.parse(this.storage.getItem('ptz'));
}
catch (e) {
return [];
}
}

async getOtherSettings(): Promise<Setting[]> {
const isDoorbell = !!this.providedInterfaces?.includes(ScryptedInterface.BinarySensor);

Expand All @@ -358,6 +367,18 @@ class OnvifCamera extends RtspSmartCamera implements ObjectDetector, Intercom, V
value: this.storage.getItem('onvifDoorbellEvent'),
placeholder: 'EventName'
},
{
key: 'ptz',
title: 'Pan/Tilt/Zoom',
type: 'string',
multiple: true,
choices: [
'Pan',
'Tilt',
'Zoom',
],
value: this.getPtz(),
}
];

if (!isDoorbell) {
Expand Down Expand Up @@ -389,23 +410,55 @@ class OnvifCamera extends RtspSmartCamera implements ObjectDetector, Intercom, V
if (twoWay || doorbell)
interfaces.push(ScryptedInterface.Intercom);

const ptzString = this.storage.getItem('ptz');
try {
const ptz = JSON.parse(ptzString) as string[];
this.ptzCapabilities = {
pan: ptz.includes('Pan'),
tilt: ptz.includes('Tilt'),
zoom: ptz.includes('Zoom'),
}
interfaces.push(ScryptedInterface.PanTiltZoom);
}
catch (e) {
this.ptzCapabilities = undefined;
}

this.provider.updateDevice(this.nativeId, this.name, interfaces, type);
this.onDeviceEvent(ScryptedInterface.Settings, undefined);
}

async putSetting(key: string, value: string) {
async putSetting(key: string, value: any) {
this.client = undefined;
this.rtspMediaStreamOptions = undefined;

this.updateDeviceInfo();

if (key !== 'onvifDoorbell' && key !== 'onvifTwoWay')
if (key !== 'onvifDoorbell' && key !== 'onvifTwoWay' && key !== 'ptz')
return super.putSetting(key, value);

if (key === 'ptz')
value = JSON.stringify(value);

this.storage.setItem(key, value);
this.updateDevice();
}

async ptzCommand(command: PanTiltZoomCommand) {
const client = await this.getClient();
return new Promise<void>((r, f) => {
client.cam.relativeMove({
x: command.pan,
y: command.tilt,
zoom: command.zoom,
}, (e, result, xml) => {
if (e)
return f(e);
r();
})
})
}

async startIntercom(media: MediaObject) {
const options = await this.getConstructedVideoStreamOptions();
const stream = options[0];
Expand Down Expand Up @@ -531,6 +584,7 @@ class OnvifProvider extends RtspProvider implements DeviceDiscovery {
const username = settings.username?.toString();
const password = settings.password?.toString();
const skipValidate = settings.skipValidate === 'true';
let ptzCapabilities: string[];
if (!skipValidate) {
try {
const api = await connectCameraAPI(httpAddress, username, password, this.console, undefined);
Expand All @@ -545,6 +599,12 @@ class OnvifProvider extends RtspProvider implements DeviceDiscovery {
}

settings.newCamera = info.model;

api.cam.services.find((s: any) => s.namespace === 'http://www.onvif.org/ver20/ptz/wsdl');
ptzCapabilities = [
'Pan',
'Tilt',
];
}
catch (e) {
this.console.error('Error adding ONVIF camera', e);
Expand Down Expand Up @@ -576,6 +636,9 @@ class OnvifProvider extends RtspProvider implements DeviceDiscovery {
intercom.intercomClient?.client.destroy();
}

if (ptzCapabilities)
device.putSetting('ptz', ptzCapabilities);

return nativeId;
}

Expand Down

0 comments on commit 83af0c5

Please sign in to comment.