mirror of
https://github.com/koush/scrypted.git
synced 2026-09-27 22:20:39 +01:00
Merge branch 'main' of github.com:koush/scrypted
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -11,9 +11,6 @@
|
||||
[submodule "external/werift"]
|
||||
path = external/werift
|
||||
url = ../../koush/werift-webrtc
|
||||
[submodule "sdk/developer.scrypted.app"]
|
||||
path = sdk/developer.scrypted.app
|
||||
url = ../../koush/developer.scrypted.app
|
||||
[submodule "plugins/sample-cameraprovider"]
|
||||
path = plugins/sample-cameraprovider
|
||||
url = ../../koush/scrypted-sample-cameraprovider
|
||||
|
||||
@@ -128,6 +128,9 @@ class DiagnosticsPlugin extends ScryptedDeviceBase implements Settings {
|
||||
await device.sendNotification('Scrypted Diagnostics', {
|
||||
body: 'Body',
|
||||
subtitle: 'Subtitle',
|
||||
android: {
|
||||
channel: 'diagnostics',
|
||||
}
|
||||
}, mo);
|
||||
|
||||
this.warnStep(console, 'Check the device for the notification.');
|
||||
|
||||
@@ -111,6 +111,12 @@ export class HomeKitPlugin extends ScryptedDeviceBase implements MixinProvider,
|
||||
hide: true,
|
||||
description: 'The last home hub to request a recording. Internally used to determine if a streaming request is coming from remote wifi.',
|
||||
},
|
||||
autoAdd: {
|
||||
title: "Auto enable",
|
||||
description: "Automatically enable this mixin on new devices.",
|
||||
type: 'boolean',
|
||||
defaultValue: true,
|
||||
},
|
||||
});
|
||||
mergedDevices = new Set<string>();
|
||||
|
||||
@@ -218,7 +224,8 @@ export class HomeKitPlugin extends ScryptedDeviceBase implements MixinProvider,
|
||||
|
||||
try {
|
||||
const mixins = (device.mixins || []).slice();
|
||||
if (!mixins.includes(this.id)) {
|
||||
const autoAdd = this.storageSettings.values.autoAdd ?? true;
|
||||
if (!mixins.includes(this.id) && autoAdd) {
|
||||
// don't sync this by default, as it's solely for automations
|
||||
if (device.type === ScryptedDeviceType.Notifier)
|
||||
continue;
|
||||
|
||||
@@ -58,6 +58,7 @@ class ReolinkCamera extends RtspSmartCamera implements Camera, DeviceProvider, R
|
||||
videoStreamOptions: Promise<UrlMediaStreamOptions[]>;
|
||||
motionTimeout: NodeJS.Timeout;
|
||||
siren: ReolinkCameraSiren;
|
||||
batteryTimeout: NodeJS.Timeout;
|
||||
|
||||
storageSettings = new StorageSettings(this, {
|
||||
doorbell: {
|
||||
@@ -181,6 +182,7 @@ class ReolinkCamera extends RtspSmartCamera implements Camera, DeviceProvider, R
|
||||
}
|
||||
const api = this.getClient();
|
||||
const deviceInfo = await api.getDeviceInfo();
|
||||
this.console.log('deviceInfo', JSON.stringify(deviceInfo));
|
||||
this.storageSettings.values.deviceInfo = deviceInfo;
|
||||
await this.updateAbilities();
|
||||
await this.updateDevice();
|
||||
@@ -222,7 +224,7 @@ class ReolinkCamera extends RtspSmartCamera implements Camera, DeviceProvider, R
|
||||
let abilities;
|
||||
try {
|
||||
abilities = await api.getAbility();
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
abilities = await apiWithToken.getAbility();
|
||||
}
|
||||
this.storageSettings.values.abilities = abilities;
|
||||
@@ -293,6 +295,11 @@ class ReolinkCamera extends RtspSmartCamera implements Camera, DeviceProvider, R
|
||||
&& this.storageSettings.values.abilities?.value?.Ability?.supportAudioAlarm?.ver !== 0;
|
||||
}
|
||||
|
||||
hasBattery() {
|
||||
const batteryConfigVer = this.storageSettings.values.abilities?.value?.Ability?.abilityChn?.[this.getRtspChannel()]?.battery?.ver ?? 0;
|
||||
return batteryConfigVer > 0;
|
||||
}
|
||||
|
||||
async updateDevice() {
|
||||
const interfaces = this.provider.getInterfaces();
|
||||
let type = ScryptedDeviceType.Camera;
|
||||
@@ -318,8 +325,31 @@ class ReolinkCamera extends RtspSmartCamera implements Camera, DeviceProvider, R
|
||||
}
|
||||
if (this.hasSiren())
|
||||
interfaces.push(ScryptedInterface.DeviceProvider);
|
||||
if (this.hasBattery()) {
|
||||
interfaces.push(ScryptedInterface.Battery, ScryptedInterface.Online);
|
||||
this.startBatteryCheckInterval();
|
||||
}
|
||||
|
||||
await this.provider.updateDevice(this.nativeId, name, interfaces, type);
|
||||
await this.provider.updateDevice(this.nativeId, this.name ?? name, interfaces, type);
|
||||
}
|
||||
|
||||
startBatteryCheckInterval() {
|
||||
if (this.batteryTimeout) {
|
||||
clearInterval(this.batteryTimeout);
|
||||
}
|
||||
|
||||
this.batteryTimeout = setInterval(async () => {
|
||||
const api = this.getClientWithToken();
|
||||
|
||||
try {
|
||||
const { batteryPercent, sleep } = await api.getBatteryInfo();
|
||||
this.batteryLevel = batteryPercent;
|
||||
this.online = !sleep;
|
||||
}
|
||||
catch (e) {
|
||||
this.console.log('Error in getting battery info', e);
|
||||
}
|
||||
}, 1000 * 60 * 30);
|
||||
}
|
||||
|
||||
async reboot() {
|
||||
@@ -625,7 +655,7 @@ class ReolinkCamera extends RtspSmartCamera implements Camera, DeviceProvider, R
|
||||
// 1: support main/extern/sub stream
|
||||
// 2: support main/sub stream
|
||||
|
||||
const live = this.storageSettings.values.abilities?.value?.Ability?.abilityChn?.[0].live?.ver;
|
||||
const live = this.storageSettings.values.abilities?.value?.Ability?.abilityChn?.[this.getRtspChannel()].live?.ver;
|
||||
const [rtmpMain, rtmpExt, rtmpSub, rtspMain, rtspSub] = streams;
|
||||
streams.splice(0, streams.length);
|
||||
|
||||
@@ -634,7 +664,7 @@ class ReolinkCamera extends RtspSmartCamera implements Camera, DeviceProvider, R
|
||||
// 1: main stream enc type is H265
|
||||
|
||||
// anecdotally, encoders of type h265 do not have a working RTMP main stream.
|
||||
const mainEncType = this.storageSettings.values.abilities?.value?.Ability?.abilityChn?.[0].mainEncType?.ver;
|
||||
const mainEncType = this.storageSettings.values.abilities?.value?.Ability?.abilityChn?.[this.getRtspChannel()].mainEncType?.ver;
|
||||
|
||||
if (live === 2) {
|
||||
if (mainEncType === 1) {
|
||||
@@ -819,7 +849,7 @@ class ReolinkProvider extends RtspProvider {
|
||||
ai = await api.getAiState();
|
||||
try {
|
||||
abilities = await api.getAbility();
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
abilities = await apiWithToken.getAbility();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +232,46 @@ export class ReolinkCameraClient {
|
||||
this.console.error('error during call to getDeviceInfo', error);
|
||||
throw new Error('error during call to getDeviceInfo');
|
||||
}
|
||||
return response.body?.[0]?.value?.DevInfo;
|
||||
|
||||
const deviceInfo: DevInfo = await response.body?.[0]?.value?.DevInfo;
|
||||
|
||||
// Will need to check if it's valid for NVR and NVR_WIFI
|
||||
if (!['HOMEHUB', 'NVR', 'NVR_WIFI'].includes(deviceInfo.exactType)) {
|
||||
return deviceInfo;
|
||||
}
|
||||
|
||||
// If the device is listed as homehub, fetch the channel specific information
|
||||
url.search = '';
|
||||
const body = [
|
||||
{ cmd: "GetChnTypeInfo", action: 0, param: { channel: this.channelId } },
|
||||
{ cmd: "GetChannelstatus", action: 0, param: {} },
|
||||
]
|
||||
|
||||
const additionalInfoResponse = await this.requestWithLogin({
|
||||
url,
|
||||
method: 'POST',
|
||||
responseType: 'json'
|
||||
}, this.createReadable(body));
|
||||
|
||||
const chnTypeInfo = additionalInfoResponse?.body?.find(elem => elem.cmd === 'GetChnTypeInfo');
|
||||
const chnStatus = additionalInfoResponse?.body?.find(elem => elem.cmd === 'GetChannelstatus');
|
||||
|
||||
if (chnTypeInfo?.value) {
|
||||
deviceInfo.firmVer = chnTypeInfo.value.firmVer;
|
||||
deviceInfo.model = chnTypeInfo.value.typeInfo;
|
||||
deviceInfo.pakSuffix = chnTypeInfo.value.pakSuffix;
|
||||
}
|
||||
|
||||
if (chnStatus?.value) {
|
||||
const specificChannelStatus = chnStatus.value?.status?.find(elem => elem.channel === this.channelId);
|
||||
|
||||
if (specificChannelStatus) {
|
||||
deviceInfo.name = specificChannelStatus.name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return deviceInfo;
|
||||
}
|
||||
|
||||
async getPtzPresets(): Promise<PtzPreset[]> {
|
||||
@@ -391,4 +430,39 @@ export class ReolinkCameraClient {
|
||||
data: response.body,
|
||||
};
|
||||
}
|
||||
|
||||
async getBatteryInfo() {
|
||||
const url = new URL(`http://${this.host}/api.cgi`);
|
||||
|
||||
const body = [
|
||||
{
|
||||
cmd: "GetBatteryInfo",
|
||||
action: 0,
|
||||
param: { channel: this.channelId }
|
||||
},
|
||||
{
|
||||
cmd: "GetChannelstatus",
|
||||
}
|
||||
];
|
||||
|
||||
const response = await this.requestWithLogin({
|
||||
url,
|
||||
responseType: 'json',
|
||||
method: 'POST',
|
||||
}, this.createReadable(body));
|
||||
|
||||
const error = response.body?.find(elem => elem.error)?.error;
|
||||
if (error) {
|
||||
this.console.error('error during call to getBatteryInfo', error);
|
||||
}
|
||||
|
||||
const batteryInfoEntry = response.body.find(entry => entry.cmd === 'GetBatteryInfo')?.value?.Battery;
|
||||
const channelStatusEntry = response.body.find(entry => entry.cmd === 'GetChannelstatus')?.value?.status
|
||||
?.find(chStatus => chStatus.channel === this.channelId)
|
||||
|
||||
return {
|
||||
batteryPercent: batteryInfoEntry?.batteryPercent,
|
||||
sleep: channelStatusEntry?.sleep === 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
214
sdk/README.md
214
sdk/README.md
@@ -1,213 +1 @@
|
||||
# Table of Contents
|
||||
* [Getting Started](#getting-started)
|
||||
* [Typescript Sample Setup](#typescript-sample-setup)
|
||||
* [Creating a Switch](#creating-a-switch)
|
||||
* [Core Concepts](#core-concepts)
|
||||
* [Interfaces](#interfaces)
|
||||
* [Events](#events)
|
||||
* [Creating Multiple Devices](#creating-multiple-devices)
|
||||
* [Full Reference](/modules)
|
||||
* [Sample Plugins](https://github.com/koush/scrypted/tree/main/plugins)
|
||||
* [Camera Provider Sample](https://github.com/koush/scrypted-sample-cameraprovider)
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
# Getting Started
|
||||
|
||||
The quickest way to get started is to check out the the [Typescript sample](https://github.com/koush/scrypted-vscode-typescript) and open it in Visual Studio Code. The setup instructions can be found in the readme for the [project](https://github.com/koush/scrypted-vscode-typescript).
|
||||
|
||||
<br/>
|
||||
|
||||
## Typescript Sample Setup
|
||||
|
||||
These instructions can be followed on your preferred development machine, and do not need to be run on the Scrypted Server itself. The Scrypted SDK can deploy and **debug** plugins running on a remote server. For example, the VS Code development environment can be running on a Mac, while the server is running on a Raspberry Pi.
|
||||
|
||||
1. npm install
|
||||
2. Open this plugin director yin VS Code.
|
||||
3. Edit `.vscode/settings.json` to point to the IP address of your Scrypted server. The default is `127.0.0.1`, your local machine.
|
||||
4. Press Launch (green arrow button in the Run and Debug sidebar) to start debugging.
|
||||
* The VS Code `Terminal` area may show an authentication failure and prompt you to log in to the Scrypted Management Console with `npx scrypted login`. You will only need to do this once. You can then relaunch afterwards.
|
||||
|
||||
<p align="center">
|
||||
<img width="538" alt="image" src="https://user-images.githubusercontent.com/73924/151676616-c730eb56-26dd-466d-b7f5-25783300b3bc.png">
|
||||
</p>
|
||||
<br/>
|
||||
|
||||
## Creating a Switch
|
||||
|
||||
The aforementioned sample will create a single switch device.
|
||||
|
||||
```typescript
|
||||
import axios from 'axios';
|
||||
import { OnOff, ScryptedDeviceBase } from '@scrypted/sdk';
|
||||
|
||||
console.log('Hello World. This will create a virtual OnOff device.');
|
||||
// OnOff is a simple binary switch. See "interfaces" in package.json
|
||||
// to add support for more capabilities, like Brightness or Lock.
|
||||
|
||||
class TypescriptLight extends ScryptedDeviceBase implements OnOff {
|
||||
constructor() {
|
||||
super();
|
||||
this.on = this.on || false;
|
||||
}
|
||||
async turnOff() {
|
||||
this.console.log('turnOff was called!');
|
||||
this.on = false;
|
||||
}
|
||||
async turnOn() {
|
||||
// set a breakpoint here.
|
||||
this.console.log('turnOn was called!');
|
||||
|
||||
this.console.log("Let's pretend to perform a web request on an API that would turn on a light.");
|
||||
const ip = await axios.get('http://jsonip.com');
|
||||
this.console.log(`my ip: ${ip.data.ip}`);
|
||||
|
||||
this.on = true;
|
||||
}
|
||||
}
|
||||
|
||||
export default TypescriptLight;
|
||||
```
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
# Core Concepts
|
||||
|
||||
Devices the core entry points and objects within Scrypted. A device can be a physical device, a virtual device, a provider of other devices (like a hub), a webhook, etc. Devices have two primary properties: Interfaces and Events.
|
||||
<br/>
|
||||
|
||||
## Interfaces
|
||||
|
||||
Interfaces are how devices expose their capabilities to Scrypted. An OnOff interface represents a binary switch. The Brightness interface represents a light that can be dimmed. The ColorSettingRgb interface indicates the light can change color. A device may expose multiple different interfaces to describe its functionality.
|
||||
|
||||
For example, given the following devices, the interfaces they would implement:
|
||||
|
||||
Outlet: OnOff,
|
||||
Dimmer Switch: OnOff, Brightness,
|
||||
Color Bulb: OnOff, Brightness, ColorSettingRgb
|
||||
Interfaces aren't only used represent characteristics of physical devices. As mentioned, they provide ways to hook into Scrypted. The HttpRequestHandler lets you add a web hook to handle incoming web requests. EventListener lets you create handlers that respond to events. DeviceProvider acts as a controller platform (like Hue or Lifx) for exposing multiple other devices to Scrypted.
|
||||
|
||||
Interfaces also provide a way to query the device state. Such as checking whether an outlet is on or off, the current brightness level, or the current color.
|
||||
|
||||
```typescript
|
||||
// Interfaces describe how the current state of a device, and can be used to modify that state.
|
||||
if (light.on) {
|
||||
light.turnOff();
|
||||
}
|
||||
else {
|
||||
light.turnOn();
|
||||
}
|
||||
```
|
||||
<br/>
|
||||
|
||||
## Events
|
||||
|
||||
Scrypted maintains the state of all connected devices. Whenever the state of an interface is updated on a device, an Event will be triggered for that particular interface.
|
||||
|
||||
For example, when a light turns on, the Light device would send an OnOff event. If a Slack message is received, the Slack device would send a MessagingEndpoint event. Setting a schedule for sunrise on weekdays would send an Alarm event on that schedule.
|
||||
|
||||
Automations subscribe to these events in your smart home setup and react accordingly.
|
||||
|
||||
```
|
||||
// Events are triggered by the device on update, and can be observed.
|
||||
light.listen('OnOff', (eventSource: ScryptedDevice, eventDetails: EventDetails, eventData: object) => {
|
||||
if (eventData) {
|
||||
log.i('The light was turned on.');
|
||||
}
|
||||
else {
|
||||
log.i('The light was turned off.');
|
||||
}
|
||||
});
|
||||
```
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
# Creating Multiple Devices
|
||||
|
||||
Most plugins will want to create multiple devices. This is done by implementing the DeviceProvider interface.
|
||||
|
||||
To do this, thep project `package.json` needs to update the `scrypted` section that describes the plugin:
|
||||
|
||||
```json
|
||||
"scrypted": {
|
||||
"name": "TypeScript Light Provider",
|
||||
"type": "DeviceProvider",
|
||||
"interfaces": [
|
||||
"DeviceProvider"
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
Then, the code is updated to support multiple lights:
|
||||
|
||||
```typescript
|
||||
import axios from 'axios';
|
||||
import sdk, { DeviceProvider, OnOff, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface } from '@scrypted/sdk';
|
||||
|
||||
class TypescriptLight extends ScryptedDeviceBase implements OnOff {
|
||||
constructor(nativeId?: string) {
|
||||
super(nativeId);
|
||||
this.on = this.on || false;
|
||||
}
|
||||
async turnOff() {
|
||||
this.console.log('turnOff was called!');
|
||||
this.on = false;
|
||||
}
|
||||
async turnOn() {
|
||||
// set a breakpoint here.
|
||||
this.console.log('turnOn was called!');
|
||||
|
||||
this.console.log("Let's pretend to perform a web request on an API that would turn on a light.");
|
||||
const ip = await axios.get('http://jsonip.com');
|
||||
this.console.log(`my ip: ${ip.data.ip}`);
|
||||
|
||||
this.on = true;
|
||||
}
|
||||
}
|
||||
|
||||
class MyDeviceProvider extends ScryptedDeviceBase implements DeviceProvider {
|
||||
constructor(nativeId?: string) {
|
||||
super(nativeId);
|
||||
|
||||
this.prepareDevices();
|
||||
}
|
||||
|
||||
async prepareDevices() {
|
||||
// "Discover" the lights provided by this provider to Scrypted.
|
||||
await sdk.deviceManager.onDevicesChanged({
|
||||
devices: [
|
||||
{
|
||||
// the native id is the unique identifier for this light within
|
||||
// your plugin.
|
||||
nativeId: 'light1',
|
||||
name: 'Light 1',
|
||||
type: ScryptedDeviceType.Light,
|
||||
interfaces: [
|
||||
ScryptedInterface.OnOff,
|
||||
]
|
||||
},
|
||||
{
|
||||
nativeId: 'light2',
|
||||
name: 'Light 1',
|
||||
type: ScryptedDeviceType.Light,
|
||||
interfaces: [
|
||||
ScryptedInterface.OnOff,
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
// After the lights are discovered, Scrypted will request the plugin create the
|
||||
// instance that can be used to control and query the light.
|
||||
getDevice(nativeId: string) {
|
||||
return new TypescriptLight(nativeId);
|
||||
}
|
||||
}
|
||||
|
||||
// Export the provider from the plugin, rather than the individual light.
|
||||
export default MyDeviceProvider;
|
||||
```
|
||||
|
||||
Running the sample will then create 3 devices: the plugin/hub and the 2 lights it controls.
|
||||
[Scrypted SDK](https://developer.scrypted.app)
|
||||
Submodule sdk/developer.scrypted.app deleted from 285ba01d8d
1911
sdk/package-lock.json
generated
1911
sdk/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,8 +12,6 @@
|
||||
"scripts": {
|
||||
"prepublishOnly": "npm run build && cd types && npm version patch && npm publish",
|
||||
"prebuild": "cd types && npm run build",
|
||||
"predocs": "npm run build",
|
||||
"docs": "typedoc && cp developer.scrypted.app/CNAME developer.scrypted.app/docs",
|
||||
"build": "rimraf dist && tsc",
|
||||
"webpack": "webpack-cli --config webpack.config.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
@@ -30,10 +28,10 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/preset-typescript": "^7.24.7",
|
||||
"adm-zip": "^0.5.14",
|
||||
"axios": "^1.7.3",
|
||||
"babel-loader": "^9.1.3",
|
||||
"@babel/preset-typescript": "^7.26.0",
|
||||
"adm-zip": "^0.5.16",
|
||||
"axios": "^1.7.7",
|
||||
"babel-loader": "^9.2.1",
|
||||
"babel-plugin-const-enum": "^1.2.0",
|
||||
"ncp": "^2.0.0",
|
||||
"raw-loader": "^4.0.2",
|
||||
@@ -41,15 +39,15 @@
|
||||
"tmp": "^0.2.3",
|
||||
"ts-loader": "^9.5.1",
|
||||
"typescript": "^5.5.4",
|
||||
"webpack": "^5.93.0",
|
||||
"webpack": "^5.95.0",
|
||||
"webpack-bundle-analyzer": "^4.10.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.1.0",
|
||||
"@types/node": "^22.8.1",
|
||||
"@types/stringify-object": "^4.0.5",
|
||||
"stringify-object": "^3.3.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typedoc": "^0.26.5"
|
||||
"typedoc": "^0.26.10"
|
||||
},
|
||||
"types": "dist/src/index.d.ts"
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"entryPoints": [
|
||||
"./src"
|
||||
],
|
||||
"sort": ["source-order"],
|
||||
"name": "Scrypted Documentation",
|
||||
"tsconfig": "./tsconfig.json",
|
||||
"out": "./developer.scrypted.app/docs",
|
||||
"categorizeByGroup": false,
|
||||
"defaultCategory": "Device Interfaces Reference",
|
||||
"excludePrivate": true,
|
||||
"disableSources": true,
|
||||
"categoryOrder": [
|
||||
"Core Reference",
|
||||
"Device Provider Reference",
|
||||
"Media Reference",
|
||||
"Webhook and Push Reference",
|
||||
"Mixin Reference",
|
||||
"WebRTC Reference"
|
||||
],
|
||||
"customCss": "./developer.scrypted.app/docs.css",
|
||||
"readme": "./README.md"
|
||||
}
|
||||
@@ -71,8 +71,8 @@ class PanTiltZoomMovement(str, Enum):
|
||||
|
||||
class ScryptedDeviceType(str, Enum):
|
||||
|
||||
API = "API"
|
||||
AirPurifier = "AirPurifier"
|
||||
API = "API"
|
||||
Automation = "Automation"
|
||||
Builtin = "Builtin"
|
||||
Camera = "Camera"
|
||||
@@ -117,9 +117,9 @@ class ScryptedInterface(str, Enum):
|
||||
BinarySensor = "BinarySensor"
|
||||
Brightness = "Brightness"
|
||||
BufferConverter = "BufferConverter"
|
||||
CO2Sensor = "CO2Sensor"
|
||||
Camera = "Camera"
|
||||
Charger = "Charger"
|
||||
CO2Sensor = "CO2Sensor"
|
||||
ColorSettingHsv = "ColorSettingHsv"
|
||||
ColorSettingRgb = "ColorSettingRgb"
|
||||
ColorSettingTemperature = "ColorSettingTemperature"
|
||||
@@ -147,8 +147,8 @@ class ScryptedInterface(str, Enum):
|
||||
Microphone = "Microphone"
|
||||
MixinProvider = "MixinProvider"
|
||||
MotionSensor = "MotionSensor"
|
||||
NOXSensor = "NOXSensor"
|
||||
Notifier = "Notifier"
|
||||
NOXSensor = "NOXSensor"
|
||||
OauthClient = "OauthClient"
|
||||
ObjectDetection = "ObjectDetection"
|
||||
ObjectDetectionGenerator = "ObjectDetectionGenerator"
|
||||
@@ -156,22 +156,22 @@ class ScryptedInterface(str, Enum):
|
||||
ObjectDetector = "ObjectDetector"
|
||||
ObjectTracker = "ObjectTracker"
|
||||
OccupancySensor = "OccupancySensor"
|
||||
OnOff = "OnOff"
|
||||
Online = "Online"
|
||||
PM10Sensor = "PM10Sensor"
|
||||
PM25Sensor = "PM25Sensor"
|
||||
OnOff = "OnOff"
|
||||
PanTiltZoom = "PanTiltZoom"
|
||||
PasswordStore = "PasswordStore"
|
||||
Pause = "Pause"
|
||||
PM10Sensor = "PM10Sensor"
|
||||
PM25Sensor = "PM25Sensor"
|
||||
PositionSensor = "PositionSensor"
|
||||
PowerSensor = "PowerSensor"
|
||||
Program = "Program"
|
||||
PushHandler = "PushHandler"
|
||||
RTCSignalingChannel = "RTCSignalingChannel"
|
||||
RTCSignalingClient = "RTCSignalingClient"
|
||||
Readme = "Readme"
|
||||
Reboot = "Reboot"
|
||||
Refresh = "Refresh"
|
||||
RTCSignalingChannel = "RTCSignalingChannel"
|
||||
RTCSignalingClient = "RTCSignalingClient"
|
||||
Scene = "Scene"
|
||||
Scriptable = "Scriptable"
|
||||
ScryptedDevice = "ScryptedDevice"
|
||||
@@ -185,13 +185,12 @@ class ScryptedInterface(str, Enum):
|
||||
Settings = "Settings"
|
||||
StartStop = "StartStop"
|
||||
StreamService = "StreamService"
|
||||
TTY = "TTY"
|
||||
TTYSettings = "TTYSettings"
|
||||
TamperSensor = "TamperSensor"
|
||||
TemperatureSetting = "TemperatureSetting"
|
||||
Thermometer = "Thermometer"
|
||||
TTY = "TTY"
|
||||
TTYSettings = "TTYSettings"
|
||||
UltravioletSensor = "UltravioletSensor"
|
||||
VOCSensor = "VOCSensor"
|
||||
VideoCamera = "VideoCamera"
|
||||
VideoCameraConfiguration = "VideoCameraConfiguration"
|
||||
VideoCameraMask = "VideoCameraMask"
|
||||
@@ -199,6 +198,7 @@ class ScryptedInterface(str, Enum):
|
||||
VideoFrameGenerator = "VideoFrameGenerator"
|
||||
VideoRecorder = "VideoRecorder"
|
||||
VideoRecorderManagement = "VideoRecorderManagement"
|
||||
VOCSensor = "VOCSensor"
|
||||
|
||||
class ScryptedMimeTypes(str, Enum):
|
||||
|
||||
@@ -211,11 +211,11 @@ class ScryptedMimeTypes(str, Enum):
|
||||
MediaStreamFeedback = "x-scrypted/x-media-stream-feedback"
|
||||
MediaStreamUrl = "text/x-media-url"
|
||||
PushEndpoint = "text/x-push-endpoint"
|
||||
RequestMediaObject = "x-scrypted/x-scrypted-request-media-object"
|
||||
RequestMediaStream = "x-scrypted/x-scrypted-request-stream"
|
||||
RTCConnectionManagement = "x-scrypted/x-scrypted-rtc-connection-management"
|
||||
RTCSignalingChannel = "x-scrypted/x-scrypted-rtc-signaling-channel"
|
||||
RTCSignalingSession = "x-scrypted/x-scrypted-rtc-signaling-session"
|
||||
RequestMediaObject = "x-scrypted/x-scrypted-request-media-object"
|
||||
RequestMediaStream = "x-scrypted/x-scrypted-request-stream"
|
||||
SchemePrefix = "x-scrypted/x-scrypted-scheme-"
|
||||
ServerId = "text/x-server-id"
|
||||
Url = "text/x-uri"
|
||||
@@ -543,20 +543,6 @@ class EventListenerOptions(TypedDict):
|
||||
mixinId: str # The EventListener will listen to events and property changes from a device or mixin that is suppressed by a mixin.
|
||||
watch: bool # This EventListener will passively watch for events, and not initiate polling.
|
||||
|
||||
class FFmpegInput(TypedDict):
|
||||
|
||||
container: str
|
||||
destinationVideoBitrate: float
|
||||
env: Any # Environment variables to set when launching FFmpeg.
|
||||
ffmpegPath: str # Path to a custom FFmpeg binary.
|
||||
h264EncoderArguments: list[str]
|
||||
h264FilterArguments: list[str]
|
||||
inputArguments: list[str]
|
||||
mediaStreamOptions: ResponseMediaStreamOptions
|
||||
url: str # The media url for this FFmpegInput.
|
||||
urls: list[str] # Alternate media urls for this FFmpegInput.
|
||||
videoDecoderArguments: list[str]
|
||||
|
||||
class FanState(TypedDict):
|
||||
|
||||
counterClockwise: bool
|
||||
@@ -574,6 +560,20 @@ class FanStatus(TypedDict):
|
||||
speed: float # Rotations per minute, if available, otherwise 0 or 1.
|
||||
swing: bool
|
||||
|
||||
class FFmpegInput(TypedDict):
|
||||
|
||||
container: str
|
||||
destinationVideoBitrate: float
|
||||
env: Any # Environment variables to set when launching FFmpeg.
|
||||
ffmpegPath: str # Path to a custom FFmpeg binary.
|
||||
h264EncoderArguments: list[str]
|
||||
h264FilterArguments: list[str]
|
||||
inputArguments: list[str]
|
||||
mediaStreamOptions: ResponseMediaStreamOptions
|
||||
url: str # The media url for this FFmpegInput.
|
||||
urls: list[str] # Alternate media urls for this FFmpegInput.
|
||||
videoDecoderArguments: list[str]
|
||||
|
||||
class HttpRequest(TypedDict):
|
||||
|
||||
aclId: str
|
||||
@@ -643,9 +643,13 @@ class MediaStreamOptions(TypedDict):
|
||||
tool: MediaStreamTool # The tool was used to write the container or will be used to read teh container. Ie, scrypted, the ffmpeg tools, gstreamer.
|
||||
video: VideoStreamOptions
|
||||
|
||||
class AndroidNotificationOptions(TypedDict):
|
||||
channel: str
|
||||
|
||||
class NotifierOptions(TypedDict):
|
||||
|
||||
actions: list[NotificationAction]
|
||||
android: AndroidNotificationOptions
|
||||
badge: str
|
||||
body: str
|
||||
bodyWithSubtitle: str
|
||||
@@ -982,10 +986,6 @@ class BufferConverter:
|
||||
pass
|
||||
|
||||
|
||||
class CO2Sensor:
|
||||
|
||||
co2ppm: float
|
||||
|
||||
class Camera:
|
||||
"""Camera devices can take still photos."""
|
||||
|
||||
@@ -1001,6 +1001,10 @@ class Charger:
|
||||
|
||||
chargeState: ChargeState
|
||||
|
||||
class CO2Sensor:
|
||||
|
||||
co2ppm: float
|
||||
|
||||
class ColorSettingHsv:
|
||||
"""ColorSettingHsv sets the color of a colored light using the HSV representation."""
|
||||
|
||||
@@ -1218,10 +1222,6 @@ class MotionSensor:
|
||||
|
||||
motionDetected: bool
|
||||
|
||||
class NOXSensor:
|
||||
|
||||
noxDensity: float
|
||||
|
||||
class Notifier:
|
||||
"""Notifier can be any endpoint that can receive messages, such as speakers, phone numbers, messaging clients, etc. The messages may optionally contain media."""
|
||||
|
||||
@@ -1229,6 +1229,10 @@ class Notifier:
|
||||
pass
|
||||
|
||||
|
||||
class NOXSensor:
|
||||
|
||||
noxDensity: float
|
||||
|
||||
class OauthClient:
|
||||
"""The OauthClient can be implemented to perform the browser based Oauth process from within a plugin."""
|
||||
|
||||
@@ -1283,6 +1287,11 @@ class OccupancySensor:
|
||||
|
||||
occupied: bool
|
||||
|
||||
class Online:
|
||||
"""Online denotes whether the device is online or unresponsive. It may be unresponsive due to being unplugged, network error, etc."""
|
||||
|
||||
online: bool
|
||||
|
||||
class OnOff:
|
||||
"""OnOff is a basic binary switch."""
|
||||
|
||||
@@ -1294,19 +1303,6 @@ class OnOff:
|
||||
pass
|
||||
|
||||
|
||||
class Online:
|
||||
"""Online denotes whether the device is online or unresponsive. It may be unresponsive due to being unplugged, network error, etc."""
|
||||
|
||||
online: bool
|
||||
|
||||
class PM10Sensor:
|
||||
|
||||
pm10Density: float
|
||||
|
||||
class PM25Sensor:
|
||||
|
||||
pm25Density: float
|
||||
|
||||
class PanTiltZoom:
|
||||
|
||||
ptzCapabilities: PanTiltZoomCapabilities
|
||||
@@ -1337,6 +1333,14 @@ class Pause:
|
||||
pass
|
||||
|
||||
|
||||
class PM10Sensor:
|
||||
|
||||
pm10Density: float
|
||||
|
||||
class PM25Sensor:
|
||||
|
||||
pm25Density: float
|
||||
|
||||
class PositionSensor:
|
||||
|
||||
position: Position
|
||||
@@ -1510,19 +1514,6 @@ class StreamService:
|
||||
pass
|
||||
|
||||
|
||||
class TTY:
|
||||
"""TTY connection offered by a remote device that can be connected to by an interactive terminal interface. Implementors should also implement StreamService to handle the actual data transfer."""
|
||||
|
||||
|
||||
pass
|
||||
|
||||
class TTYSettings:
|
||||
"""TTYSettings allows TTY backends to query plugins for modifications to the (non-)interactive terminal environment."""
|
||||
|
||||
async def getTTYSettings(self) -> Any:
|
||||
pass
|
||||
|
||||
|
||||
class TamperSensor:
|
||||
|
||||
tampered: TamperState
|
||||
@@ -1543,14 +1534,23 @@ class Thermometer:
|
||||
pass
|
||||
|
||||
|
||||
class TTY:
|
||||
"""TTY connection offered by a remote device that can be connected to by an interactive terminal interface. Implementors should also implement StreamService to handle the actual data transfer."""
|
||||
|
||||
|
||||
pass
|
||||
|
||||
class TTYSettings:
|
||||
"""TTYSettings allows TTY backends to query plugins for modifications to the (non-)interactive terminal environment."""
|
||||
|
||||
async def getTTYSettings(self) -> Any:
|
||||
pass
|
||||
|
||||
|
||||
class UltravioletSensor:
|
||||
|
||||
ultraviolet: float
|
||||
|
||||
class VOCSensor:
|
||||
|
||||
vocDensity: float
|
||||
|
||||
class VideoCamera:
|
||||
"""VideoCamera devices can capture video streams."""
|
||||
|
||||
@@ -1581,10 +1581,10 @@ class VideoClips:
|
||||
async def getVideoClip(self, videoId: str) -> MediaObject:
|
||||
pass
|
||||
|
||||
async def getVideoClipThumbnail(self, thumbnailId: str, options: VideoClipThumbnailOptions = None) -> MediaObject:
|
||||
async def getVideoClips(self, options: VideoClipOptions = None) -> list[VideoClip]:
|
||||
pass
|
||||
|
||||
async def getVideoClips(self, options: VideoClipOptions = None) -> list[VideoClip]:
|
||||
async def getVideoClipThumbnail(self, thumbnailId: str, options: VideoClipThumbnailOptions = None) -> MediaObject:
|
||||
pass
|
||||
|
||||
async def removeVideoClips(self, videoClipIds: list[str]) -> None:
|
||||
@@ -1622,6 +1622,10 @@ class VideoRecorderManagement:
|
||||
pass
|
||||
|
||||
|
||||
class VOCSensor:
|
||||
|
||||
vocDensity: float
|
||||
|
||||
class Logger:
|
||||
"""Logger is exposed via log.* to allow writing to the Scrypted log."""
|
||||
|
||||
@@ -1911,8 +1915,8 @@ class ScryptedInterfaceMethods(str, Enum):
|
||||
ptzCommand = "ptzCommand"
|
||||
getRecordedEvents = "getRecordedEvents"
|
||||
getVideoClip = "getVideoClip"
|
||||
getVideoClipThumbnail = "getVideoClipThumbnail"
|
||||
getVideoClips = "getVideoClips"
|
||||
getVideoClipThumbnail = "getVideoClipThumbnail"
|
||||
removeVideoClips = "removeVideoClips"
|
||||
setVideoStreamOptions = "setVideoStreamOptions"
|
||||
startIntercom = "startIntercom"
|
||||
@@ -2721,8 +2725,8 @@ ScryptedInterfaceDescriptors = {
|
||||
"name": "VideoClips",
|
||||
"methods": [
|
||||
"getVideoClip",
|
||||
"getVideoClipThumbnail",
|
||||
"getVideoClips",
|
||||
"getVideoClipThumbnail",
|
||||
"removeVideoClips"
|
||||
],
|
||||
"properties": []
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import stringifyObject from 'stringify-object';
|
||||
import { ScryptedInterface, ScryptedInterfaceDescriptor } from "./types.input";
|
||||
import path from 'path';
|
||||
import fs from "fs";
|
||||
import { DeclarationReflection, ProjectReflection, ReflectionKind, SomeType } from 'typedoc';
|
||||
import path from 'path';
|
||||
import stringifyObject from 'stringify-object';
|
||||
import { DeclarationReflection, ProjectReflection, ReflectionKind } from 'typedoc';
|
||||
import { ScryptedInterface, ScryptedInterfaceDescriptor } from "./types.input";
|
||||
|
||||
const schema = JSON.parse(fs.readFileSync(path.join(__dirname, '../gen/schema.json')).toString()) as ProjectReflection;
|
||||
const packageJson = require('../package.json');
|
||||
|
||||
@@ -225,6 +225,9 @@ export interface NotifierOptions {
|
||||
badge?: string;
|
||||
bodyWithSubtitle?: string;
|
||||
body?: string;
|
||||
android?: {
|
||||
channel?: string;
|
||||
}
|
||||
data?: any;
|
||||
dir?: NotificationDirection;
|
||||
lang?: string;
|
||||
|
||||
Reference in New Issue
Block a user