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

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
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.