Skip to content

Commit

Permalink
Incremented schema version to 6
Browse files Browse the repository at this point in the history
Added new commands `startRTSPLivestream`, `stopRTSPLivestream` and `isRTSPLiveStreaming` to Station
Fixed issue #15
Fixed issue #27
Updated dependency eufy-security-client to 1.2.0
Updated dependency commander to 8.2.0
Updated dependency tslog to 3.2.2
Updated dependency ws to 8.2.3
Updated dev dependencies
bropat committed Oct 17, 2021
1 parent 8d97a61 commit 373f4b8
Showing 15 changed files with 416 additions and 214 deletions.
6 changes: 5 additions & 1 deletion API_SCHEMA.md
Original file line number Diff line number Diff line change
@@ -35,4 +35,8 @@ Base schema.
## Schema 5

* Added new properties `nightvision` and `batteryIsCharging` to Device
* Added new properties `switchModeWithAccessCode`, `autoEndAlarm` and `turnOffAlarmWithButton` to Station
* Added new properties `switchModeWithAccessCode`, `autoEndAlarm` and `turnOffAlarmWithButton` to Station

## Schema 6

* Added new commands `startRTSPLivestream`, `stopRTSPLivestream` and `isRTSPLiveStreaming` to Station
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -937,6 +937,51 @@ interface {
}
```

#### Start RTSP live stream

[compatible with schema version: 6+]

```ts
interface {
messageId: string;
command: "device.start_rtsp_livestream";
serialNumber: string;
}
```

#### Stop RTSP live stream

[compatible with schema version: 6+]

```ts
interface {
messageId: string;
command: "device.stop_rtsp_livestream";
serialNumber: string;
}
```

#### Get RTSP live stream status

[compatible with schema version: 6+]

```ts
interface {
messageId: string;
command: "device.is_rtsp_livestreaming";
serialNumber: string;
}
```

Returns:

```ts
interface {
serialNumber: string;
livestreaming: boolean;
}
```

## Events

### Driver level events
379 changes: 189 additions & 190 deletions package-lock.json

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eufy-security-ws",
"version": "0.4.2",
"version": "0.5.0",
"description": "Node WebSocket server implementation to integrate Eufy Security devices",
"main": "dist/lib/index.js",
"bin": {
@@ -43,25 +43,25 @@
},
"dependencies": {
"ansi-colors": "^4.1.1",
"commander": "^8.1.0",
"eufy-security-client": "^1.1.2",
"commander": "^8.2.0",
"eufy-security-client": "^1.2.0",
"fs-extra": "^10.0.0",
"promptly": "^3.2.0",
"tslog": "^3.2.1",
"ws": "^8.2.0"
"tslog": "^3.2.2",
"ws": "^8.2.3"
},
"devDependencies": {
"@types/ansi-colors": "3.2.2",
"@types/node": "^16.4.14",
"@types/node": "^16.11.0",
"@types/node-rsa": "^1.1.1",
"@types/promptly": "^3.0.2",
"@types/ws": "^7.4.7",
"@typescript-eslint/eslint-plugin": "^4.29.2",
"@typescript-eslint/parser": "^4.29.2",
"@types/ws": "^8.2.0",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"prettier": "^2.3.2",
"ts-node": "^10.2.1",
"typescript": "^4.3.5"
"prettier": "^2.4.1",
"ts-node": "^10.3.0",
"typescript": "^4.4.4"
}
}
36 changes: 36 additions & 0 deletions src/bin/client.ts
Original file line number Diff line number Diff line change
@@ -50,6 +50,9 @@ const cmdHelp = (cmd: string): void => {
case DeviceCommand.cancelDownload:
case DeviceCommand.isLiveStreaming:
case DeviceCommand.getCommands:
case DeviceCommand.startRTSPLivestream:
case DeviceCommand.stopRTSPLivestream:
case DeviceCommand.isRTSPLiveStreaming:
console.log(`${cmd} <device_sn>`);
break;
case DeviceCommand.setProperty:
@@ -629,6 +632,39 @@ process.on("SIGTERM", handleShutdown);
cmdHelp(args[0]);
}
break;
case DeviceCommand.startRTSPLivestream:
if (args.length === 2) {
socket.send(JSON.stringify({
messageId: DeviceCommand.startRTSPLivestream.split(".")[1],
command: DeviceCommand.startRTSPLivestream,
serialNumber: args[1],
}));
} else {
cmdHelp(args[0]);
}
break;
case DeviceCommand.stopRTSPLivestream:
if (args.length === 2) {
socket.send(JSON.stringify({
messageId: DeviceCommand.stopRTSPLivestream.split(".")[1],
command: DeviceCommand.stopRTSPLivestream,
serialNumber: args[1],
}));
} else {
cmdHelp(args[0]);
}
break;
case DeviceCommand.isRTSPLiveStreaming:
if (args.length === 2) {
socket.send(JSON.stringify({
messageId: DeviceCommand.isRTSPLiveStreaming.split(".")[1],
command: DeviceCommand.isRTSPLiveStreaming,
serialNumber: args[1],
}));
} else {
cmdHelp(args[0]);
}
break;
case StationCommand.setGuardMode:
if (args.length === 3 && isNumber(args[2])) {
socket.send(JSON.stringify({
2 changes: 1 addition & 1 deletion src/lib/const.ts
Original file line number Diff line number Diff line change
@@ -5,4 +5,4 @@ export const version = require("../../package.json").version;
export const minSchemaVersion = 0;

// maximal/current schema version the server supports
export const maxSchemaVersion = 5;
export const maxSchemaVersion = 6;
3 changes: 3 additions & 0 deletions src/lib/device/command.ts
Original file line number Diff line number Diff line change
@@ -26,4 +26,7 @@ export enum DeviceCommand {
getVoices = "device.get_voices",
getCommands = "device.get_commands",
hasCommand = "device.has_command",
startRTSPLivestream = "device.start_rtsp_livestream",
stopRTSPLivestream = "device.stop_rtsp_livestream",
isRTSPLiveStreaming = "device.is_rtsp_livestreaming",
}
18 changes: 17 additions & 1 deletion src/lib/device/event.ts
Original file line number Diff line number Diff line change
@@ -21,6 +21,8 @@ export enum DeviceEvent {
downloadFinished = "download finished",
downloadVideoData = "download video data",
downloadAudioData = "download audio data",
rtspLivestreamStarted = "rtsp livestream started",
rtspLivestreamStopped = "rtsp livestream stopped",
}

export interface OutgoingEventDeviceBase extends OutgoingBaseEvent {
@@ -186,6 +188,18 @@ export interface OutgoingEventDeviceDownloadAudioData extends OutgoingEventDevic
}
}

export interface OutgoingEventDeviceRTSPLivestreamStarted extends OutgoingEventDeviceBase {
source: "device";
event: DeviceEvent.rtspLivestreamStarted;
serialNumber: string;
}

export interface OutgoingEventDeviceRTSPLivestreamStopped extends OutgoingEventDeviceBase {
source: "device";
event: DeviceEvent.rtspLivestreamStopped;
serialNumber: string;
}

export type OutgoingEventDevice =
| OutgoingEventDeviceAdded
| OutgoingEventDeviceRemoved
@@ -206,4 +220,6 @@ export type OutgoingEventDevice =
| OutgoingEventDeviceDownloadStarted
| OutgoingEventDeviceDownloadFinished
| OutgoingEventDeviceDownloadVideoData
| OutgoingEventDeviceDownloadAudioData;
| OutgoingEventDeviceDownloadAudioData
| OutgoingEventDeviceRTSPLivestreamStarted
| OutgoingEventDeviceRTSPLivestreamStopped;
17 changes: 16 additions & 1 deletion src/lib/device/incoming_message.ts
Original file line number Diff line number Diff line change
@@ -131,6 +131,18 @@ export interface IncomingCommandDeviceGetCommands extends IncomingCommandDeviceB
command: DeviceCommand.getCommands;
}

export interface IncomingCommandDeviceStartRTSPLivestream extends IncomingCommandDeviceBase {
command: DeviceCommand.startRTSPLivestream;
}

export interface IncomingCommandDeviceStopRTSPLivestream extends IncomingCommandDeviceBase {
command: DeviceCommand.stopRTSPLivestream;
}

export interface IncomingCommandDeviceIsRTSPLiveStreaming extends IncomingCommandDeviceBase {
command: DeviceCommand.isRTSPLiveStreaming;
}

export type IncomingMessageDevice =
| IncomingCommandDeviceSetStatusLed
| IncomingCommandDeviceSetAutoNightVision
@@ -157,4 +169,7 @@ export type IncomingMessageDevice =
| IncomingCommandDeviceGetVoices
| IncomingCommandDeviceHasProperty
| IncomingCommandDeviceHasCommand
| IncomingCommandDeviceGetCommands;
| IncomingCommandDeviceGetCommands
| IncomingCommandDeviceStartRTSPLivestream
| IncomingCommandDeviceStopRTSPLivestream
| IncomingCommandDeviceIsRTSPLiveStreaming;
33 changes: 32 additions & 1 deletion src/lib/device/message_handler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EufySecurity } from "eufy-security-client";
import { LivestreamAlreadyRunningError, LivestreamNotRunningError, UnknownCommandError } from "../error";
import { LivestreamAlreadyRunningError, LivestreamNotRunningError, RTSPLivestreamAlreadyRunningError, RTSPLivestreamNotRunningError, UnknownCommandError } from "../error";
import { Client } from "../server";
import { DeviceCommand } from "./command";
import { DeviceEvent } from "./event";
@@ -287,6 +287,37 @@ export class DeviceMessageHandler {
}
}
}
case DeviceCommand.startRTSPLivestream:
if (client.schemaVersion >= 6) {
if (!station.isRTSPLiveStreaming(device)) {
await station.startRTSPStream(device).catch((error) => {
throw error;
});
} else {
throw new RTSPLivestreamAlreadyRunningError(`RTSP livestream for device ${serialNumber} is already running`);
}
return { };
}
case DeviceCommand.stopRTSPLivestream:
if (client.schemaVersion >= 6) {
if (!station.isRTSPLiveStreaming(device)) {
throw new RTSPLivestreamNotRunningError(`RTSP livestream for device ${serialNumber} could not be stopped, because it is not running`);
}
await station.stopRTSPStream(device).catch((error) => {
throw error;
});
return { };
}
case DeviceCommand.isRTSPLiveStreaming:
{
if (client.schemaVersion >= 6) {
const result = station.isRTSPLiveStreaming(device);
return {
serialNumber: device.getSerial(),
livestreaming: result
};
}
}
default:
throw new UnknownCommandError(command);
}
3 changes: 3 additions & 0 deletions src/lib/device/outgoing_message.ts
Original file line number Diff line number Diff line change
@@ -28,5 +28,8 @@ export interface DeviceResultTypes {
[DeviceCommand.hasProperty]: { serialNumber?: string; exists: boolean };
[DeviceCommand.hasCommand]: { serialNumber?: string; exists: boolean };
[DeviceCommand.getCommands]: { serialNumber?: string; commands: Array<CommandName>; };
[DeviceCommand.startRTSPLivestream]: Record<string, never>;
[DeviceCommand.stopRTSPLivestream]: Record<string, never>;
[DeviceCommand.isRTSPLiveStreaming]: { serialNumber?: string; livestreaming: boolean };

}
2 changes: 1 addition & 1 deletion src/lib/driver/message_handler.ts
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ export class DriverMessageHandler {
return { result: result };
}
case DriverCommand.pollRefresh:
await driver.refreshData().catch((error) => {
await driver.refreshCloudData().catch((error) => {
throw error;
});
return { };
18 changes: 18 additions & 0 deletions src/lib/error.ts
Original file line number Diff line number Diff line change
@@ -15,6 +15,8 @@ export enum ErrorCode {
deviceInvalidCommandValue = "device_invalid_command_value",
deviceLivestreamAlreadyRunning = "device_livestream_already_running",
deviceLivestreamNotRunning = "device_livestream_not_running",
deviceRTSPLivestreamAlreadyRunning = "device_rtsp_livestream_already_running",
deviceRTSPLivestreamNotRunning = "device_rtsp_livestream_not_running",
schemaIncompatible = "schema_incompatible",
}

@@ -62,4 +64,20 @@ export class LivestreamNotRunningError extends Error {
Object.setPrototypeOf(this, new.target.prototype);
this.name = LivestreamNotRunningError.name;
}
}

export class RTSPLivestreamAlreadyRunningError extends Error {
constructor(message?: string) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
this.name = RTSPLivestreamAlreadyRunningError.name;
}
}

export class RTSPLivestreamNotRunningError extends Error {
constructor(message?: string) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
this.name = RTSPLivestreamNotRunningError.name;
}
}
28 changes: 28 additions & 0 deletions src/lib/forward.ts
Original file line number Diff line number Diff line change
@@ -240,6 +240,34 @@ export class EventForwarder {
});
});

this.clients.driver.on("station rtsp livestream start", (station: Station, device: Device) => {
const serialNumber = device.getSerial();
this.clients.clients.filter((cl) => cl.isConnected)
.forEach((client) => {
if (client.schemaVersion >= 6) {
client.sendEvent({
source: "device",
event: DeviceEvent.rtspLivestreamStarted,
serialNumber: serialNumber,
});
}
});
});

this.clients.driver.on("station rtsp livestream stop", (station: Station, device: Device) => {
const serialNumber = device.getSerial();
this.clients.clients.filter((cl) => cl.isConnected)
.forEach((client) => {
if (client.schemaVersion >= 6) {
client.sendEvent({
source: "device",
event: DeviceEvent.rtspLivestreamStopped,
serialNumber: serialNumber,
});
}
});
});

}

private forwardEvent(data: OutgoingEvent, minSchemaVersion: number, maxSchemaVersion: number = internalSchemaVersion): void {
16 changes: 10 additions & 6 deletions src/lib/server.ts
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import type WebSocket from "ws";
import { Logger } from "tslog";
import { EventEmitter, once } from "events";
import { Server as HttpServer, createServer, IncomingMessage as HttpIncomingMessage } from "http";
import { DeviceNotFoundError, EufySecurity, InvalidCountryCodeError, InvalidLanguageCodeError, InvalidPropertyValueError, libVersion, NotConnectedError, NotSupportedError, ReadOnlyPropertyError, StationNotFoundError, WrongStationError, PropertyNotSupportedError, InvalidPropertyError, InvalidCommandValueError } from "eufy-security-client";
import { DeviceNotFoundError, EufySecurity, InvalidCountryCodeError, InvalidLanguageCodeError, InvalidPropertyValueError, libVersion, NotSupportedError, ReadOnlyPropertyError, StationNotFoundError, WrongStationError, PropertyNotSupportedError, InvalidPropertyError, InvalidCommandValueError } from "eufy-security-client";

import { EventForwarder } from "./forward";
import type * as OutgoingMessages from "./outgoing_message";
@@ -12,7 +12,7 @@ import { version, minSchemaVersion, maxSchemaVersion } from "./const";
import { DeviceMessageHandler } from "./device/message_handler";
import { StationMessageHandler } from "./station/message_handler";
import { IncomingMessageStation } from "./station/incoming_message";
import { BaseError, ErrorCode, LivestreamAlreadyRunningError, LivestreamNotRunningError, SchemaIncompatibleError, UnknownCommandError } from "./error";
import { BaseError, ErrorCode, LivestreamAlreadyRunningError, LivestreamNotRunningError, RTSPLivestreamAlreadyRunningError, RTSPLivestreamNotRunningError, SchemaIncompatibleError, UnknownCommandError } from "./error";
import { Instance } from "./instance";
import { IncomingMessageDevice } from "./device/incoming_message";
import { ServerCommand } from "./command";
@@ -113,10 +113,6 @@ export class Client {
this.logger.error("Message error", error);
return this.sendResultError(msg.messageId, ErrorCode.stationNotFound);
}
if (error instanceof NotConnectedError) {
this.logger.error("Message error", error);
return this.sendResultError(msg.messageId, ErrorCode.stationNotConnected);
}
if (error instanceof NotSupportedError) {
this.logger.error("Message error", error);
return this.sendResultError(msg.messageId, ErrorCode.deviceNotSupported);
@@ -161,6 +157,14 @@ export class Client {
this.logger.error("Message error", error);
return this.sendResultError(msg.messageId, ErrorCode.deviceInvalidCommandValue);
}
if (error instanceof RTSPLivestreamAlreadyRunningError) {
this.logger.error("Message error", error);
return this.sendResultError(msg.messageId, ErrorCode.deviceRTSPLivestreamAlreadyRunning);
}
if (error instanceof RTSPLivestreamNotRunningError) {
this.logger.error("Message error", error);
return this.sendResultError(msg.messageId, ErrorCode.deviceRTSPLivestreamNotRunning);
}

this.logger.error("Unexpected error", error as Error);
this.sendResultError(msg.messageId, ErrorCode.unknownError);

0 comments on commit 373f4b8

Please sign in to comment.