Skip to content

Commit

Permalink
Added event states for camera (motion detected, person detected)
Browse files Browse the repository at this point in the history
Added event states for entry sensor (open/closed)
Added event states for motion sensor (motion detected)
Added event states for doorbell (motion detected, person detected, ringing)
Added event states for indoor camera (motion detected, person detected, crying detected, sound detected, pet detected)
Added entry sensor state (online, offline, etc.)
Added entry sensor low battery
Added motion sensor state (online, offline, etc.)
Added motion sensor low battery
Added keypad state (online, offline, etc.)
Added keypad low battery
  • Loading branch information
Patrick Broetto committed Dec 5, 2020
1 parent 81bc22f commit 1756b24
Show file tree
Hide file tree
Showing 24 changed files with 1,830 additions and 394 deletions.
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
engine-strict=true
56 changes: 51 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,45 @@ One Adapter instance will show all devices from one Eufy Cloud account and allow
## Features

* Base station:
* Change guard mode
* States:
* Configured Guard mode
* Current Guard mode
* ...
* Actions:
* Change guard mode
* Camera:
* Start livestream (rtmp)
* Stop livestream (rtmp)
* States:
* Online / offline etc.
* Battery %
* ...
* Actions:
* Start livestream (rtmp)
* Stop livestream (rtmp)
* Events:
* Motion detected
* Person detected
* Ringing (only Doorbell)
* Crying detected (only Indoor cameras)
* Sound detected (only Indoor cameras)
* Pet detected (only Indoor cameras)
* Sensor
* Entry sensor:
* States:
* Online / offline etc.
* Low battery
* Events:
* Open / closed
* Motion sensor:
* States:
* Online / offline etc.
* Low battery
* Events:
* Motion detected
* Keypad:
* States:
* Online / offline etc.
* Low battery
* Two factor authentication (token renewal needs manual intervention)
* Push notification support
* Basic P2P communication functionality:
* event: Alarm mode change
* more to come...
Expand All @@ -60,7 +93,20 @@ Best is to set the adapter to Debug log mode (Instances -> Expert mode -> Column

## Changelog

### 0.0.4
### 0.0.5 (2020-12-05)
* (bropat) Added event states for camera (motion detected, person detected)
* (bropat) Added event states for entry sensor (open/closed)
* (bropat) Added event states for motion sensor (motion detected)
* (bropat) Added event states for doorbell (motion detected, person detected, ringing)
* (bropat) Added event states for indoor camera (motion detected, person detected, crying detected, sound detected, pet detected)
* (bropat) Added entry sensor state (online, offline, etc.)
* (bropat) Added entry sensor low battery
* (bropat) Added motion sensor state (online, offline, etc.)
* (bropat) Added motion sensor low battery
* (bropat) Added keypad state (online, offline, etc.)
* (bropat) Added keypad low battery

### 0.0.4 (2020-12-03)
* (bropat) Better exception handling
* (bropat) Fixed push token handling
* (bropat) Added push connection retry mechanism
Expand Down
6 changes: 6 additions & 0 deletions admin/index_m.html
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@
<label for="maxLivestreamDuration" class="translate">Max camera livestream duration (sec)</label>
</div>
</div>
<div class="row">
<div class="col s6 input-field">
<input class="value" min="1" max="120" type="number" id="eventDuration" />
<label for="eventDuration" class="translate">Time in seconds before event reset</label>
</div>
</div>
<!--<div class="row">
<div class="col s6 input-field">
<input class="value" type="number" id="verificationCode" />
Expand Down
22 changes: 11 additions & 11 deletions admin/words.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,16 @@ systemDictionary = {
"pl": "Maksymalny czas trwania transmisji na żywo z kamery (s)",
"zh-cn": "相机直播的最大持续时间(秒)"
},
"Two factor authentication verification method": {
"en": "Two factor authentication verification method",
"de": "Überprüfungsmethode für die Zwei-Faktor-Authentifizierung",
"ru": "Метод проверки двухфакторной аутентификации",
"pt": "Método de verificação de autenticação de dois fatores",
"nl": "Verificatiemethode met twee factoren",
"fr": "Méthode de vérification de l'authentification à deux facteurs",
"it": "Metodo di verifica dell'autenticazione a due fattori",
"es": "Método de verificación de autenticación de dos factores",
"pl": "Metoda weryfikacji uwierzytelniania dwuskładnikowego",
"zh-cn": "两因素认证验证方法"
"Time in seconds before event reset": {
"en": "Time in seconds before event reset",
"de": "Zeit in Sekunden vor dem Zurücksetzen des Ereignisses",
"ru": "Время в секундах до сброса события",
"pt": "Tempo em segundos antes da redefinição do evento",
"nl": "Tijd in seconden voordat de gebeurtenis wordt gereset",
"fr": "Temps en secondes avant la réinitialisation de l'événement",
"it": "Tempo in secondi prima del ripristino dell'evento",
"es": "Tiempo en segundos antes del reinicio del evento",
"pl": "Czas w sekundach do zresetowania zdarzenia",
"zh-cn": "事件重置之前的时间(以秒为单位)"
}
};
4 changes: 4 additions & 0 deletions build/lib/eufy-security/eufy-security.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const types_1 = require("./http/types");
const station_1 = require("./http/station");
const register_1 = require("./push/register");
const client_1 = require("./push/client");
const utils_1 = require("./utils");
class EufySecurity extends events_1.EventEmitter {
constructor(adapter) {
super();
Expand Down Expand Up @@ -338,6 +339,9 @@ class EufySecurity extends events_1.EventEmitter {
}
stationParameterChanged(station, type, value) {
this.log.debug(`EufySecurity.stationParameterChanged(): station: ${station.getSerial()} type: ${type} value: ${value}`);
if (type == types_1.ParamType.GUARD_MODE)
//TODO: if configured guard mode was changed to SCHEDULE (2) we get the correct current mode, but we change the effective guard mode on next http data refresh... Get it asap!
utils_1.setStateChangedAsync(this.adapter, station.getStateID(types_1.StationStateID.GUARD_MODE), value);
}
deviceParameterChanged(device, type, value) {
this.log.debug(`EufySecurity.deviceParameterChanged(): device: ${device.getSerial()} type: ${type} value: ${value}`);
Expand Down
97 changes: 96 additions & 1 deletion build/lib/eufy-security/http/device.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnkownDevice = exports.Keypad = exports.Lock = exports.Sensor = exports.FloodlightCamera = exports.DoorbellCamera = exports.Camera = exports.Device = void 0;
exports.UnkownDevice = exports.Keypad = exports.Lock = exports.MotionSensor = exports.EntrySensor = exports.Sensor = exports.FloodlightCamera = exports.DoorbellCamera = exports.Camera = exports.Device = void 0;
const types_1 = require("./types");
const parameter_1 = require("./parameter");
const events_1 = require("events");
Expand Down Expand Up @@ -64,6 +64,20 @@ class Device extends events_1.EventEmitter {
return true;
return false;
}
static hasBattery(type) {
if (type == types_1.DeviceType.CAMERA ||
type == types_1.DeviceType.CAMERA2 ||
type == types_1.DeviceType.CAMERA_E ||
type == types_1.DeviceType.CAMERA2C ||
type == types_1.DeviceType.BATTERY_DOORBELL ||
type == types_1.DeviceType.BATTERY_DOORBELL_2 ||
type == types_1.DeviceType.CAMERA2C_PRO ||
type == types_1.DeviceType.CAMERA2_PRO ||
type == types_1.DeviceType.SOLO_CAMERA ||
type == types_1.DeviceType.SOLO_CAMERA_PRO)
return true;
return false;
}
static isStation(type) {
if (type == types_1.DeviceType.STATION)
return true;
Expand All @@ -85,6 +99,14 @@ class Device extends events_1.EventEmitter {
return true;
return false;
}
static isIndoorCamera(type) {
if (type == types_1.DeviceType.INDOOR_CAMERA ||
type == types_1.DeviceType.INDOOR_CAMERA_1080 ||
type == types_1.DeviceType.INDOOR_PT_CAMERA ||
type == types_1.DeviceType.INDOOR_PT_CAMERA_1080)
return true;
return false;
}
static isFloodLight(type) {
return types_1.DeviceType.FLOODLIGHT == type;
}
Expand Down Expand Up @@ -210,6 +232,12 @@ class Device extends events_1.EventEmitter {
isMotionSensor() {
return Device.isMotionSensor(this.device.device_type);
}
isIndoorCamera() {
return Device.isIndoorCamera(this.device.device_type);
}
hasBattery() {
return Device.hasBattery(this.device.device_type);
}
getDeviceKey() {
return this.device.station_sn + this.device.device_channel;
}
Expand Down Expand Up @@ -425,16 +453,83 @@ class Sensor extends Device {
}
}
exports.Sensor = Sensor;
class EntrySensor extends Sensor {
isSensorOpen() {
if (this.getParameter(types_2.CommandType.CMD_ENTRY_SENSOR_STATUS) === "1")
return true;
return false;
}
getSensorChangeTime() {
return this.getParameter(types_2.CommandType.CMD_ENTRY_SENSOR_CHANGE_TIME);
}
isBatteryLow() {
if (this.getParameter(types_2.CommandType.CMD_ENTRY_SENSOR_BAT_STATE) === "1")
return true;
return false;
}
getState() {
return Number.parseInt(this.getParameter(types_2.CommandType.CMD_GET_DEV_STATUS));
}
}
exports.EntrySensor = EntrySensor;
class MotionSensor extends Sensor {
//TODO: CMD_MOTION_SENSOR_ENABLE_LED = 1607
//TODO: CMD_MOTION_SENSOR_ENTER_USER_TEST_MODE = 1613
//TODO: CMD_MOTION_SENSOR_EXIT_USER_TEST_MODE = 1610
//TODO: CMD_MOTION_SENSOR_SET_CHIRP_TONE = 1611
//TODO: CMD_MOTION_SENSOR_SET_PIR_SENSITIVITY = 1609
//TODO: CMD_MOTION_SENSOR_WORK_MODE = 1612
static isMotionDetected(millis) {
const delta = new Date().getUTCMilliseconds() - millis;
if (delta < this.MOTION_COOLDOWN_MS) {
return { motion: true, cooldown_ms: this.MOTION_COOLDOWN_MS - delta };
}
return { motion: false, cooldown_ms: 0 };
}
isMotionDetected() {
return MotionSensor.isMotionDetected(this.getMotionSensorPIREvent());
}
getState() {
return Number.parseInt(this.getParameter(types_2.CommandType.CMD_GET_DEV_STATUS));
}
getMotionSensorPIREvent() {
//TODO: Implement P2P Control Event over active station connection
return Number.parseInt(this.getParameter(types_2.CommandType.CMD_MOTION_SENSOR_PIR_EVT));
}
isBatteryLow() {
if (this.getParameter(types_2.CommandType.CMD_MOTION_SENSOR_BAT_STATE) === "1")
return true;
return false;
}
}
exports.MotionSensor = MotionSensor;
MotionSensor.MOTION_COOLDOWN_MS = 120000;
class Lock extends Device {
getStateChannel() {
return "locks";
}
}
exports.Lock = Lock;
class Keypad extends Device {
//TODO: CMD_KEYPAD_BATTERY_CHARGER_STATE = 1655
//TODO: CMD_KEYPAD_BATTERY_TEMP_STATE = 1654
//TODO: CMD_KEYPAD_GET_PASSWORD = 1657
//TODO: CMD_KEYPAD_GET_PASSWORD_LIST = 1662
//TODO: CMD_KEYPAD_IS_PSW_SET = 1670
//TODO: CMD_KEYPAD_PSW_OPEN = 1664
//TODO: CMD_KEYPAD_SET_CUSTOM_MAP = 1660
//TODO: CMD_KEYPAD_SET_PASSWORD = 1650
getStateChannel() {
return "keypads";
}
getState() {
return Number.parseInt(this.getParameter(types_2.CommandType.CMD_GET_DEV_STATUS));
}
isBatteryLow() {
if (this.getParameter(types_2.CommandType.CMD_KEYPAD_BATTERY_CAP_STATE) === "1")
return true;
return false;
}
}
exports.Keypad = Keypad;
class UnkownDevice extends Device {
Expand Down
9 changes: 7 additions & 2 deletions build/lib/eufy-security/http/types.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StationStateID = exports.CameraStateID = exports.DeviceStateID = exports.VerfyCodeTypes = exports.ResponseErrorCode = exports.GuardMode = exports.AlarmMode = exports.ParamType = exports.DeviceType = void 0;
exports.IndoorCameraStateID = exports.DoorbellStateID = exports.KeyPadStateID = exports.MotionSensorStateID = exports.EntrySensorStateID = exports.StationStateID = exports.CameraStateID = exports.DeviceStateID = exports.VerfyCodeTypes = exports.ResponseErrorCode = exports.GuardMode = exports.AlarmMode = exports.ParamType = exports.DeviceType = void 0;
var DeviceType;
(function (DeviceType) {
//List retrieved from com.oceanwing.battery.cam.binder.model.QueryDeviceData
Expand Down Expand Up @@ -128,5 +128,10 @@ exports.DeviceStateID = {
HARDWARE_VERSION: "hardware_version",
SOFTWARE_VERSION: "software_version",
};
exports.CameraStateID = Object.assign(Object.assign({}, exports.DeviceStateID), { STATE: "state", MAC_ADDRESS: "mac_address", LAST_CAMERA_URL: "last_camera_url", LIVESTREAM: "livestream", START_STREAM: "start_stream", STOP_STREAM: "stop_stream", BATTERY: "battery", BATTERY_TEMPERATURE: "battery_temperature", LAST_CHARGE_TOTAL_EVENTS: "last_charge_total_events", LAST_CHARGE_USED_DAYS: "last_charge_used_days", LAST_CHARGE_FILTERED_EVENTS: "last_charge_filtered_events", LAST_CHARGE_SAVED_EVENTS: "last_charge_saved_events", WIFI_RSSI: "wifi_rssi" });
exports.CameraStateID = Object.assign(Object.assign({}, exports.DeviceStateID), { STATE: "state", MAC_ADDRESS: "mac_address", LAST_CAMERA_URL: "last_camera_url", LIVESTREAM: "livestream", START_STREAM: "start_stream", STOP_STREAM: "stop_stream", BATTERY: "battery", BATTERY_TEMPERATURE: "battery_temperature", LAST_CHARGE_TOTAL_EVENTS: "last_charge_total_events", LAST_CHARGE_USED_DAYS: "last_charge_used_days", LAST_CHARGE_FILTERED_EVENTS: "last_charge_filtered_events", LAST_CHARGE_SAVED_EVENTS: "last_charge_saved_events", WIFI_RSSI: "wifi_rssi", MOTION_DETECTED: "motion_detected", PERSON_DETECTED: "person_detected", PERSON_IDENTIFIED: "person_identified", CAPTURED_PIC_URL: "captured_pic_url" });
exports.StationStateID = Object.assign(Object.assign({}, exports.DeviceStateID), { GUARD_MODE: "guard_mode", CURRENT_MODE: "current_mode", IP_ADDRESS: "ip_address", LAN_IP_ADDRESS: "lan_ip_address", MAC_ADDRESS: "mac_address" });
exports.EntrySensorStateID = Object.assign(Object.assign({}, exports.DeviceStateID), { STATE: "state", SENSOR_OPEN: "sensor_open", LOW_BATTERY: "low_battery", SENSOR_CHANGE_TIME: "sensor_change_time" });
exports.MotionSensorStateID = Object.assign(Object.assign({}, exports.DeviceStateID), { STATE: "state", LOW_BATTERY: "low_battery", MOTION_DETECTED: "motion_detected" });
exports.KeyPadStateID = Object.assign(Object.assign({}, exports.DeviceStateID), { STATE: "state", LOW_BATTERY: "low_battery" });
exports.DoorbellStateID = Object.assign(Object.assign({}, exports.CameraStateID), { RINGING: "ringing" });
exports.IndoorCameraStateID = Object.assign(Object.assign({}, exports.CameraStateID), { CRYING_DETECTED: "crying_detected", SOUND_DETECTED: "sound_detected", PET_DETECTED: "pet_detected" });
5 changes: 3 additions & 2 deletions build/lib/eufy-security/p2p/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class EufyP2PClientProtocol extends events_1.EventEmitter {
const msgSeqNumber = this.seqNumber++;
const commandHeader = utils_1.buildCommandHeader(msgSeqNumber, commandType);
const data = Buffer.concat([commandHeader, payload]);
this.log.debug(`EufyP2PClientProtocol.connect(): Sending commandType: ${commandType} with seqNum: ${msgSeqNumber}...`);
this.log.debug(`EufyP2PClientProtocol.sendCommand(): Sending commandType: ${commandType} with seqNum: ${msgSeqNumber}...`);
utils_1.sendMessage(this.socket, this.address, types_1.RequestMessageType.DATA, data);
// -> NOTE:
// -> We could wait for an ACK and then continue (sync)
Expand Down Expand Up @@ -160,6 +160,7 @@ class EufyP2PClientProtocol extends events_1.EventEmitter {
// We have already seen this message, skip!
// This can happen because the device is sending the message till it gets a ACK
// which can take some time.
this.sendAck(dataTypeBuffer, seqNo);
return;
}
this.seenSeqNo[dataType] = seqNo;
Expand All @@ -181,7 +182,7 @@ class EufyP2PClientProtocol extends events_1.EventEmitter {
// Note: data === 65420 when e.g. data mode is already set (guardMode=0, setting guardMode=0 => 65420)
// Note: data === 65430 when there is an error (sending data to a channel which do not exist)
const commandStr = types_1.CommandType[commandId];
this.log.debug(`EufyP2PClientProtocol.handleData(): commandId: ${commandStr} (${commandId}) - data: ${data}`);
this.log.debug(`EufyP2PClientProtocol.handleData(): commandId: ${commandStr} (${commandId}) - data: ${data} - msg: ${msg.toString("hex")}`);
}
else if (dataType === "BINARY") {
//this.parseBinaryMessage(seqNo, msg);
Expand Down
Loading

0 comments on commit 1756b24

Please sign in to comment.