UNPKG

eufy-security-client-fork

Version:

Client to comunicate with Eufy-Security devices

994 lines 303 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Station = void 0; const tiny_typed_emitter_1 = require("tiny-typed-emitter"); const types_1 = require("./types"); const parameter_1 = require("./parameter"); const utils_1 = require("./utils"); const session_1 = require("../p2p/session"); const types_2 = require("../p2p/types"); const device_1 = require("./device"); const utils_2 = require("../p2p/utils"); const error_1 = require("../error"); const types_3 = require("../push/types"); const error_2 = require("./error"); const utils_3 = require("../utils"); class Station extends tiny_typed_emitter_1.TypedEmitter { constructor(api, station) { super(); this.properties = {}; this.rawProperties = {}; this.ready = false; this.currentDelay = 0; this.terminating = false; this.p2pConnectionType = types_2.P2PConnectionType.QUICKEST; this.api = api; this.rawStation = station; this.log = api.getLog(); this.p2pSession = new session_1.P2PClientProtocol(this.rawStation, this.api); this.p2pSession.on("connect", (address) => this.onConnect(address)); this.p2pSession.on("close", () => this.onDisconnect()); this.p2pSession.on("timeout", () => this.onTimeout()); this.p2pSession.on("command", (result) => this.onCommandResponse(result)); this.p2pSession.on("alarm mode", (mode) => this.onAlarmMode(mode)); this.p2pSession.on("camera info", (cameraInfo) => this.onCameraInfo(cameraInfo)); this.p2pSession.on("download started", (channel, metadata, videoStream, audioStream) => this.onStartDownload(channel, metadata, videoStream, audioStream)); this.p2pSession.on("download finished", (channel) => this.onFinishDownload(channel)); this.p2pSession.on("livestream started", (channel, metadata, videoStream, audioStream) => this.onStartLivestream(channel, metadata, videoStream, audioStream)); this.p2pSession.on("livestream stopped", (channel) => this.onStopLivestream(channel)); this.p2pSession.on("livestream error", (channel, error) => this.onErrorLivestream(channel, error)); this.p2pSession.on("wifi rssi", (channel, rssi) => this.onWifiRssiChanged(channel, rssi)); this.p2pSession.on("rtsp livestream started", (channel) => this.onStartRTSPLivestream(channel)); this.p2pSession.on("rtsp livestream stopped", (channel) => this.onStopRTSPLivestream(channel)); this.p2pSession.on("rtsp url", (channel, rtspUrl) => this.onRTSPUrl(channel, rtspUrl)); this.p2pSession.on("parameter", (channel, param, value) => this.onParameter(channel, param, value)); this.p2pSession.on("runtime state", (channel, batteryLevel, temperature) => this.onRuntimeState(channel, batteryLevel, temperature)); this.p2pSession.on("charging state", (channel, chargeType, batteryLevel) => this.onChargingState(channel, chargeType, batteryLevel)); this.p2pSession.on("floodlight manual switch", (channel, enabled) => this.onFloodlightManualSwitch(channel, enabled)); this.p2pSession.on("alarm delay", (alarmDelayEvent, alarmDelay) => this.onAlarmDelay(alarmDelayEvent, alarmDelay)); this.p2pSession.on("alarm armed", () => this.onAlarmArmed()); this.p2pSession.on("alarm event", (alarmEvent) => this.onAlarmEvent(alarmEvent)); this.p2pSession.on("talkback started", (channel, talkbackStream) => this.onTalkbackStarted(channel, talkbackStream)); this.p2pSession.on("talkback stopped", (channel) => this.onTalkbackStopped(channel)); this.p2pSession.on("talkback error", (channel, error) => this.onTalkbackError(channel, error)); this.p2pSession.on("secondary command", (result) => this.onSecondaryCommandResponse(result)); this.p2pSession.on("shake alarm", (channel, event) => this.onDeviceShakeAlarm(channel, event)); this.p2pSession.on("911 alarm", (channel, event) => this.onDevice911Alarm(channel, event)); this.p2pSession.on("jammed", (channel) => this.onDeviceJammed(channel)); this.p2pSession.on("low battery", (channel) => this.onDeviceLowBattery(channel)); this.p2pSession.on("wrong try-protect alarm", (channel) => this.onDeviceWrongTryProtectAlarm(channel)); this.update(this.rawStation); this.ready = true; setImmediate(() => { this.emit("ready", this); }); } getStateID(state, level = 2) { switch (level) { case 0: return `${this.getSerial()}`; case 1: return `${this.getSerial()}.${this.getStateChannel()}`; default: if (state) return `${this.getSerial()}.${this.getStateChannel()}.${state}`; throw new Error("No state value passed."); } } getStateChannel() { return "station"; } getRawStation() { return this.rawStation; } update(station, cloudOnlyProperties = false) { this.rawStation = station; this.p2pSession.updateRawStation(station); const metadata = this.getPropertiesMetadata(); for (const property of Object.values(metadata)) { if (this.rawStation[property.key] !== undefined && typeof property.key === "string") { this.updateProperty(property.name, this.rawStation[property.key]); } else if (this.properties[property.name] === undefined && property.default !== undefined && !this.ready) { this.updateProperty(property.name, property.default); } } if (!cloudOnlyProperties) { this.rawStation.params.forEach(param => { this.updateRawProperty(param.param_type, param.param_value); }); } this.log.debug("Normalized Properties", { stationSN: this.getSerial(), properties: this.properties }); } updateProperty(name, value) { if ((this.properties[name] !== undefined && this.properties[name] !== value) || this.properties[name] === undefined) { this.properties[name] = value; if (this.ready) this.emit("property changed", this, name, value); return true; } return false; } updateRawProperties(values) { Object.keys(values).forEach(paramtype => { const param_type = Number.parseInt(paramtype); this.updateRawProperty(param_type, values[param_type]); }); } updateRawProperty(type, value) { const parsedValue = parameter_1.ParameterHelper.readValue(type, value, this.log); if ((this.rawProperties[type] !== undefined && this.rawProperties[type] !== parsedValue) || this.rawProperties[type] === undefined) { this.rawProperties[type] = parsedValue; if (this.ready) { this.emit("raw property changed", this, type, this.rawProperties[type]); try { if (type === types_1.ParamType.GUARD_MODE) { this.emit("guard mode", this, Number.parseInt(parsedValue)); } else if (type === types_2.CommandType.CMD_GET_ALARM_MODE) { this.emit("current mode", this, Number.parseInt(parsedValue)); } } catch (error) { this.log.error("Number conversion error", error); } } const metadata = this.getPropertiesMetadata(); for (const property of Object.values(metadata)) { if (property.key === type) { try { this.updateProperty(property.name, this.convertRawPropertyValue(property, this.rawProperties[type])); } catch (error) { if (error instanceof error_2.PropertyNotSupportedError) { this.log.debug("Property not supported error", error); } else { this.log.error("Property error", error); } } } } return true; } return false; } convertRawPropertyValue(property, value) { try { switch (property.key) { case types_2.CommandType.CMD_GET_HUB_LAN_IP: return value !== undefined ? ((0, utils_2.isPrivateIp)(value) ? value : "") : ""; case types_2.CommandType.CMD_SET_ARMING: return Number.parseInt(value !== undefined ? value : "-1"); case types_2.CommandType.CMD_GET_ALARM_MODE: { const guard_mode = this.getGuardMode(); return Number.parseInt(value !== undefined ? value : guard_mode !== undefined && guard_mode !== types_1.GuardMode.SCHEDULE && guard_mode !== types_1.GuardMode.GEO ? guard_mode : types_1.GuardMode.UNKNOWN.toString()); } case types_2.CommandType.CMD_HUB_NOTIFY_MODE: { switch (property.name) { case types_1.PropertyName.StationNotificationSwitchModeSchedule: if (!(0, utils_1.isGreaterEqualMinVersion)("2.1.1.6", this.getSoftwareVersion())) { return value !== undefined ? (value === "1" ? true : false) : false; } return value !== undefined ? (0, utils_1.isNotificationSwitchMode)(Number.parseInt(value), types_1.NotificationSwitchMode.SCHEDULE) : false; case types_1.PropertyName.StationNotificationSwitchModeGeofence: if (!(0, utils_1.isGreaterEqualMinVersion)("2.1.1.6", this.getSoftwareVersion())) { throw new error_2.PropertyNotSupportedError(`Property ${property.name} not supported for station ${this.getSerial()} with software version ${this.getSoftwareVersion()}`); } return value !== undefined ? (0, utils_1.isNotificationSwitchMode)(Number.parseInt(value), types_1.NotificationSwitchMode.GEOFENCE) : false; case types_1.PropertyName.StationNotificationSwitchModeApp: if (!(0, utils_1.isGreaterEqualMinVersion)("2.1.1.6", this.getSoftwareVersion())) { throw new error_2.PropertyNotSupportedError(`Property ${property.name} not supported for station ${this.getSerial()} with software version ${this.getSoftwareVersion()}`); } return value !== undefined ? (0, utils_1.isNotificationSwitchMode)(Number.parseInt(value), types_1.NotificationSwitchMode.APP) : false; case types_1.PropertyName.StationNotificationSwitchModeKeypad: if (!(0, utils_1.isGreaterEqualMinVersion)("2.1.1.6", this.getSoftwareVersion())) { throw new error_2.PropertyNotSupportedError(`Property ${property.name} not supported for station ${this.getSerial()} with software version ${this.getSoftwareVersion()}`); } return value !== undefined ? (0, utils_1.isNotificationSwitchMode)(Number.parseInt(value), types_1.NotificationSwitchMode.KEYPAD) : false; } } case types_2.CommandType.CMD_HUB_NOTIFY_ALARM: return value !== undefined ? (value === "1" ? true : false) : false; case types_2.CommandType.CMD_HUB_ALARM_TONE: try { return value !== undefined ? Number.parseInt(value) : 1; } catch (error) { this.log.error("Convert CMD_HUB_ALARM_TONE Error:", { property: property, value: value, error: error }); return 1; } case types_2.CommandType.CMD_SET_HUB_SPK_VOLUME: try { return value !== undefined ? Number.parseInt(value) : 26; } catch (error) { this.log.error("Convert CMD_SET_HUB_SPK_VOLUME Error:", { property: property, value: value, error: error }); return 26; } case types_2.CommandType.CMD_SET_PROMPT_VOLUME: try { return value !== undefined ? Number.parseInt(value) : 26; } catch (error) { this.log.error("Convert CMD_SET_PROMPT_VOLUME Error:", { property: property, value: value, error: error }); return 26; } case types_2.CommandType.CMD_SET_HUB_OSD: try { return value !== undefined ? Number.parseInt(value) : 0; } catch (error) { this.log.error("Convert CMD_SET_HUB_OSD Error:", { property: property, value: value, error: error }); return 0; } case types_2.CommandType.CMD_SET_HUB_ALARM_AUTO_END: return value !== undefined ? value !== "0" ? false : true : false; case types_2.CommandType.CMD_SET_HUB_ALARM_CLOSE: return value !== undefined ? value === "1" ? false : true : false; } if (property.type === "number") { const numericProperty = property; try { return value !== undefined ? Number.parseInt(value) : (numericProperty.default !== undefined ? numericProperty.default : (numericProperty.min !== undefined ? numericProperty.min : 0)); } catch (error) { this.log.warn("PropertyMetadataNumeric Convert Error:", { property: property, value: value, error: error }); return numericProperty.default !== undefined ? numericProperty.default : (numericProperty.min !== undefined ? numericProperty.min : 0); } } else if (property.type === "boolean") { const booleanProperty = property; try { return value !== undefined ? (value === "1" || value.toLowerCase() === "true" ? true : false) : (booleanProperty.default !== undefined ? booleanProperty.default : false); } catch (error) { this.log.warn("PropertyMetadataBoolean Convert Error:", { property: property, value: value, error: error }); return booleanProperty.default !== undefined ? booleanProperty.default : false; } } else if (property.type === "string") { const stringProperty = property; return value !== undefined ? value : (stringProperty.default !== undefined ? stringProperty.default : ""); } } catch (error) { this.log.error("Convert Error:", { property: property, value: value, error: error }); } return value; } getPropertyMetadata(name) { const property = this.getPropertiesMetadata()[name]; if (property !== undefined) return property; throw new error_2.InvalidPropertyError(`Property ${name} invalid`); } getPropertyValue(name) { if (name === types_1.PropertyName.StationCurrentMode) { const guard_mode = this.properties[types_1.PropertyName.StationGuardMode]; return this.properties[types_1.PropertyName.StationCurrentMode] !== undefined ? this.properties[types_1.PropertyName.StationCurrentMode] : guard_mode !== undefined && guard_mode !== types_1.GuardMode.SCHEDULE && guard_mode !== types_1.GuardMode.GEO ? guard_mode : types_1.GuardMode.UNKNOWN; } return this.properties[name]; } hasPropertyValue(name) { return this.getPropertyValue(name) !== undefined; } getRawProperty(type) { return this.rawProperties[type]; } getRawProperties() { return this.rawProperties; } getProperties() { return this.properties; } getPropertiesMetadata() { let metadata = types_1.StationProperties[this.getDeviceType()]; if (metadata === undefined) { metadata = types_1.StationProperties[types_1.DeviceType.STATION]; } if (this.hasDeviceWithType(types_1.DeviceType.KEYPAD)) { metadata[types_1.PropertyName.StationGuardMode] = types_1.StationGuardModeKeyPadProperty; metadata[types_1.PropertyName.StationCurrentMode] = types_1.StationCurrentModeKeyPadProperty; metadata[types_1.PropertyName.StationSwitchModeWithAccessCode] = types_1.StationSwitchModeWithAccessCodeProperty; metadata[types_1.PropertyName.StationAutoEndAlarm] = types_1.StationAutoEndAlarmProperty; metadata[types_1.PropertyName.StationTurnOffAlarmWithButton] = types_1.StationTurnOffAlarmWithButtonProperty; } return metadata; } hasProperty(name) { return this.getPropertiesMetadata()[name] !== undefined; } getCommands() { const commands = types_1.StationCommands[this.getDeviceType()]; if (commands === undefined) return []; return commands; } hasCommand(name) { return this.getCommands().includes(name); } isStation() { return this.rawStation.device_type == types_1.DeviceType.STATION; } isDeviceStation() { return this.rawStation.device_type != types_1.DeviceType.STATION; } isIntegratedDevice() { var _a, _b; if (device_1.Device.isLock(this.getDeviceType()) || device_1.Device.isSmartDrop(this.getDeviceType()) || device_1.Device.isSmartSafe(this.getDeviceType())) { if (((_a = this.rawStation.devices) === null || _a === void 0 ? void 0 : _a.length) === 1) return ((_b = this.rawStation.devices[0]) === null || _b === void 0 ? void 0 : _b.device_sn) === this.rawStation.station_sn; else return true; } return device_1.Device.isWiredDoorbellDual(this.getDeviceType()) || device_1.Device.isFloodLight(this.getDeviceType()) || device_1.Device.isWiredDoorbell(this.getDeviceType()) || device_1.Device.isIndoorCamera(this.getDeviceType()) || device_1.Device.isSoloCameras(this.getDeviceType()); } getDeviceType() { return this.rawStation.device_type; } getHardwareVersion() { return this.rawStation.main_hw_version; } getMACAddress() { return this.rawStation.wifi_mac; } getModel() { return this.rawStation.station_model; } getName() { return this.rawStation.station_name; } getSerial() { return this.rawStation.station_sn; } getSoftwareVersion() { return this.rawStation.main_sw_version; } getIPAddress() { return this.rawStation.ip_addr; } getLANIPAddress() { return this.getPropertyValue(types_1.PropertyName.StationLANIpAddress); } getGuardMode() { return this.getPropertyValue(types_1.PropertyName.StationGuardMode); } getCurrentMode() { const guard_mode = this.getGuardMode(); return this.getPropertyValue(types_1.PropertyName.StationCurrentMode) !== undefined ? this.getPropertyValue(types_1.PropertyName.StationCurrentMode) : guard_mode !== undefined && guard_mode !== types_1.GuardMode.SCHEDULE && guard_mode !== types_1.GuardMode.GEO ? guard_mode : types_1.GuardMode.UNKNOWN; } processPushNotification(message) { if (message.type !== undefined && message.event_type !== undefined) { if (message.event_type === types_3.CusPushEvent.MODE_SWITCH && message.station_sn === this.getSerial()) { this.log.info("Received push notification for changing guard mode", { guard_mode: message.station_guard_mode, current_mode: message.station_current_mode, stationSN: message.station_sn }); try { if (message.station_guard_mode !== undefined) this.updateRawProperty(types_1.ParamType.GUARD_MODE, message.station_guard_mode.toString()); if (message.station_current_mode !== undefined) this.updateRawProperty(types_2.CommandType.CMD_GET_ALARM_MODE, message.station_current_mode.toString()); } catch (error) { this.log.debug(`Station ${message.station_sn} MODE_SWITCH event (${message.event_type}) - Error:`, error); } } else if (message.event_type === types_3.CusPushEvent.ALARM && message.station_sn === this.getSerial() && !this.isStation()) { this.log.info("Received push notification for alarm event", { stationSN: message.station_sn, alarmType: message.alarm_type }); if (message.alarm_type !== undefined) this.emit("alarm event", this, message.alarm_type); } } } isConnected() { return this.p2pSession.isConnected(); } close() { this.terminating = true; this.log.info(`Disconnect from station ${this.getSerial()}`); if (this.reconnectTimeout) { clearTimeout(this.reconnectTimeout); this.reconnectTimeout = undefined; } if (this.p2pSession.isConnected()) { this.p2pSession.close(); } } isEnergySavingDevice() { return this.p2pSession.isEnergySavingDevice(); } async connect() { this.log.debug(`Connecting to station ${this.getSerial()}...`, { p2pConnectionType: types_2.P2PConnectionType[this.p2pConnectionType] }); this.p2pSession.setConnectionType(this.p2pConnectionType); this.p2pSession.connect(); } onFinishDownload(channel) { this.emit("download finish", this, channel); } onStartDownload(channel, metadata, videoStream, audioStream) { this.emit("download start", this, channel, metadata, videoStream, audioStream); } onStopLivestream(channel) { this.emit("livestream stop", this, channel); } onErrorLivestream(channel, error) { this.emit("livestream error", this, channel, error); } onStartLivestream(channel, metadata, videoStream, audioStream) { this.emit("livestream start", this, channel, metadata, videoStream, audioStream); } onStopRTSPLivestream(channel) { this.emit("rtsp livestream stop", this, channel); } onStartRTSPLivestream(channel) { this.emit("rtsp livestream start", this, channel); } onWifiRssiChanged(channel, rssi) { this.emit("wifi rssi", this, channel, rssi); } onRTSPUrl(channel, rtspUrl) { this.emit("rtsp url", this, channel, rtspUrl); } onParameter(channel, param, value) { const params = {}; params[param] = parameter_1.ParameterHelper.readValue(param, value, this.log); this.emit("raw device property changed", this._getDeviceSerial(channel), params); } onAlarmDelay(alarmDelayEvent, alarmDelay) { this.emit("alarm delay event", this, alarmDelayEvent, alarmDelay); } onAlarmArmed() { this.emit("alarm armed event", this); } onAlarmEvent(alarmEvent) { this.emit("alarm event", this, alarmEvent); } async setGuardMode(mode) { const propertyData = { name: types_1.PropertyName.StationGuardMode, value: mode }; if (!this.hasProperty(propertyData.name)) { throw new error_1.NotSupportedError(`This functionality is not implemented or supported by ${this.getSerial()}`); } const property = this.getPropertyMetadata(propertyData.name); (0, utils_3.validValue)(property, mode); this.log.debug(`Sending guard mode command to station ${this.getSerial()} with value: ${types_1.GuardMode[mode]}`); if (((0, utils_1.isGreaterEqualMinVersion)("2.0.7.9", this.getSoftwareVersion()) && !device_1.Device.isIntegratedDeviceBySn(this.getSerial())) || device_1.Device.isSoloCameraBySn(this.getSerial())) { this.log.debug(`Using CMD_SET_PAYLOAD for station ${this.getSerial()}`, { main_sw_version: this.getSoftwareVersion() }); await this.p2pSession.sendCommandWithStringPayload({ commandType: types_2.CommandType.CMD_SET_PAYLOAD, value: JSON.stringify({ "account_id": this.rawStation.member.admin_user_id, "cmd": types_2.CommandType.CMD_SET_ARMING, "mValue3": 0, "payload": { "mode_type": mode, "user_name": this.rawStation.member.nick_name } }), channel: Station.CHANNEL }, { property: propertyData }); } else { this.log.debug(`Using CMD_SET_ARMING for station ${this.getSerial()}`); await this.p2pSession.sendCommandWithInt({ commandType: types_2.CommandType.CMD_SET_ARMING, value: mode, strValue: this.rawStation.member.admin_user_id, channel: Station.CHANNEL }, { property: propertyData }); } } async getCameraInfo() { this.log.debug(`Sending get camera infos command to station ${this.getSerial()}`); await this.p2pSession.sendCommandWithInt({ commandType: types_2.CommandType.CMD_CAMERA_INFO, value: 255, channel: Station.CHANNEL }); } async getStorageInfo() { this.log.debug(`Sending get storage info command to station ${this.getSerial()}`); //TODO: Verify channel! Should be 255... await this.p2pSession.sendCommandWithIntString({ commandType: types_2.CommandType.CMD_SDINFO_EX, value: 0, valueSub: 0, strValue: this.rawStation.member.admin_user_id }); } async onAlarmMode(mode) { this.log.info(`Alarm mode for station ${this.getSerial()} changed to: ${types_1.AlarmMode[mode]}`); this.updateRawProperty(types_2.CommandType.CMD_GET_ALARM_MODE, mode.toString()); const armDelay = this.getArmDelay(mode); if (armDelay > 0) { this.emit("alarm arm delay event", this, armDelay); } // Trigger refresh Guard Mode await this.getCameraInfo(); } getArmDelay(mode) { let propertyName; switch (mode) { case types_1.AlarmMode.HOME: propertyName = types_1.PropertyName.StationHomeSecuritySettings; break; case types_1.AlarmMode.AWAY: propertyName = types_1.PropertyName.StationAwaySecuritySettings; break; case types_1.AlarmMode.CUSTOM1: propertyName = types_1.PropertyName.StationCustom1SecuritySettings; break; case types_1.AlarmMode.CUSTOM2: propertyName = types_1.PropertyName.StationCustom2SecuritySettings; break; case types_1.AlarmMode.CUSTOM3: propertyName = types_1.PropertyName.StationCustom3SecuritySettings; break; } if (propertyName !== undefined && this.hasPropertyValue(propertyName) && this.getPropertyValue(propertyName) !== "") { const settings = this.getPropertyValue(propertyName); if (settings.count_down_arm.channel_list.length > 0 && settings.count_down_arm.delay_time > 0) { return settings.count_down_arm.delay_time; } } return 0; } _getDeviceSerial(channel) { if (this.rawStation.devices) for (const device of this.rawStation.devices) { if (device.device_channel === channel) return device.device_sn; } return ""; } onCameraInfo(cameraInfo) { this.log.debug("Got camera infos", { station: this.getSerial(), cameraInfo: cameraInfo }); const devices = {}; cameraInfo.params.forEach(param => { if (param.dev_type === Station.CHANNEL || param.dev_type === Station.CHANNEL_INDOOR || this.isIntegratedDevice()) { this.updateRawProperty(param.param_type, param.param_value); if (param.param_type === types_2.CommandType.CMD_GET_ALARM_MODE) { if (this.getDeviceType() !== types_1.DeviceType.STATION) // Trigger refresh Guard Mode this.api.refreshStationData(); } if (this.isIntegratedDevice()) { const device_sn = this.getSerial(); if (!devices[device_sn]) { devices[device_sn] = {}; } devices[device_sn][param.param_type] = parameter_1.ParameterHelper.readValue(param.param_type, param.param_value, this.log); } } else { const device_sn = this._getDeviceSerial(param.dev_type); if (device_sn !== "") { if (!devices[device_sn]) { devices[device_sn] = {}; } devices[device_sn][param.param_type] = parameter_1.ParameterHelper.readValue(param.param_type, param.param_value, this.log); } } }); Object.keys(devices).forEach(device => { this.emit("raw device property changed", device, devices[device]); }); } onCommandResponse(result) { this.log.debug("Got p2p command response", { station: this.getSerial(), commandType: result.command_type, channel: result.channel, returnCodeName: types_2.ErrorCode[result.return_code], returnCode: result.return_code, customData: result.customData }); this.emit("command result", this, result); } onSecondaryCommandResponse(result) { this.log.debug("Got p2p secondary command response", { station: this.getSerial(), commandType: result.command_type, channel: result.channel, returnCode: result.return_code, customData: result.customData }); this.emit("secondary command result", this, result); } onConnect(address) { this.terminating = false; this.resetCurrentDelay(); this.log.info(`Connected to station ${this.getSerial()} on host ${address.host} and port ${address.port}`); this.emit("connect", this); } onDisconnect() { this.log.info(`Disconnected from station ${this.getSerial()}`); this.emit("close", this); if (!this.isEnergySavingDevice() && !this.terminating) this.scheduleReconnect(); } onTimeout() { this.log.info(`Timeout connecting to station ${this.getSerial()}`); this.emit("connection error", this, new error_1.StationConnectTimeoutError()); this.scheduleReconnect(); } getCurrentDelay() { const delay = this.currentDelay == 0 ? 5000 : this.currentDelay; if (this.currentDelay < 60000) this.currentDelay += 10000; if (this.currentDelay >= 60000 && this.currentDelay < 600000) this.currentDelay += 60000; return delay; } resetCurrentDelay() { this.currentDelay = 0; } scheduleReconnect() { if (!this.reconnectTimeout) { const delay = this.getCurrentDelay(); this.log.debug(`Schedule reconnect to station ${this.getSerial()}...`, { delay: delay }); this.reconnectTimeout = setTimeout(async () => { this.reconnectTimeout = undefined; this.connect(); }, delay); } } async rebootHUB() { const commandData = { name: types_1.CommandName.StationReboot }; if (!this.hasCommand(types_1.CommandName.StationReboot)) { throw new error_1.NotSupportedError(`This functionality is not implemented or supported by ${this.getSerial()}`); } this.log.debug(`Sending reboot command to station ${this.getSerial()}`); await this.p2pSession.sendCommandWithInt({ commandType: types_2.CommandType.CMD_HUB_REBOOT, value: 0, strValue: this.rawStation.member.admin_user_id, channel: Station.CHANNEL }, { command: commandData }); } async setStatusLed(device, value) { const propertyData = { name: types_1.PropertyName.DeviceStatusLed, value: value }; if (device.getStationSerial() !== this.getSerial()) { throw new error_1.WrongStationError(`Device ${device.getSerial()} is not managed by this station ${this.getSerial()}`); } if (!device.hasProperty(propertyData.name)) { throw new error_1.NotSupportedError(`This functionality is not implemented or supported by ${device.getSerial()}`); } this.log.debug(`Sending status led command to station ${this.getSerial()} for device ${device.getSerial()} with value: ${value}`); if (device.isCamera2Product() || device.getDeviceType() === types_1.DeviceType.CAMERA || device.getDeviceType() === types_1.DeviceType.CAMERA_E) { await this.p2pSession.sendCommandWithIntString({ commandType: types_2.CommandType.CMD_DEV_LED_SWITCH, value: value === true ? 1 : 0, valueSub: device.getChannel(), strValue: this.rawStation.member.admin_user_id, channel: device.getChannel() }, { property: propertyData }); await this.p2pSession.sendCommandWithIntString({ commandType: types_2.CommandType.CMD_LIVEVIEW_LED_SWITCH, value: value === true ? 1 : 0, valueSub: device.getChannel(), strValue: this.rawStation.member.admin_user_id, channel: device.getChannel() }, { property: propertyData }); } else if (device.getDeviceType() === types_1.DeviceType.FLOODLIGHT_CAMERA_8423 || device.getDeviceType() === types_1.DeviceType.FLOODLIGHT) { await this.p2pSession.sendCommandWithIntString({ commandType: types_2.CommandType.CMD_DEV_LED_SWITCH, value: value === true ? 1 : 0, valueSub: device.getChannel(), strValue: this.rawStation.member.admin_user_id, channel: device.getChannel() }, { property: propertyData }); } else if (device.isIndoorCamera() || device.isFloodLight()) { await this.p2pSession.sendCommandWithStringPayload({ commandType: types_2.CommandType.CMD_DOORBELL_SET_PAYLOAD, value: JSON.stringify({ "commandType": types_2.CommandType.CMD_INDOOR_LED_SWITCH, "data": { "enable": 0, "index": 0, "status": 0, "type": 0, "value": value === true ? 1 : 0, "voiceID": 0, "zonecount": 0, "mediaAccountInfo": { "deviceChannel": device.getChannel(), "device_sn": device.getSerial(), "device_type": -1, "mDeviceName": device.getName(), "mDidStr": this.rawStation.p2p_did, "mHubSn": this.getSerial(), "mInitStr": this.rawStation.app_conn, "mReceiveVersion": "", "mTimeInfo": "", "mVersionName": "" }, "transaction": `${new Date().getTime()}` }, }), channel: device.getChannel() }, { property: propertyData }); } else if (device.isSoloCameras()) { await this.p2pSession.sendCommandWithStringPayload({ commandType: types_2.CommandType.CMD_DOORBELL_SET_PAYLOAD, value: JSON.stringify({ "commandType": types_2.CommandType.CMD_INDOOR_LED_SWITCH, "data": { "enable": 0, "index": 0, "status": 0, "type": 0, "url": "", "value": value === true ? 1 : 0, "voiceID": 0, "zonecount": 0, "mediaAccountInfo": { "deviceChannel": device.getChannel(), "device_sn": device.getSerial(), "device_type": -1, "mDeviceName": device.getName(), "mDidStr": this.rawStation.p2p_did, "mHubSn": this.getSerial(), "mInitStr": this.rawStation.app_conn, "mReceiveVersion": "", "mTimeInfo": "", "mVersionName": "" }, "transaction": `${new Date().getTime()}`, } }), channel: device.getChannel() }, { property: propertyData }); } else if (device.isBatteryDoorbell() || device.isWiredDoorbellDual()) { await this.p2pSession.sendCommandWithStringPayload({ commandType: types_2.CommandType.CMD_SET_PAYLOAD, value: JSON.stringify({ "account_id": this.rawStation.member.admin_user_id, "cmd": types_2.CommandType.CMD_BAT_DOORBELL_SET_LED_ENABLE, "mValue3": 0, "payload": { "light_enable": value === true ? 1 : 0 } }), channel: device.getChannel() }, { property: propertyData }); } else if (device.isWiredDoorbell()) { await this.p2pSession.sendCommandWithStringPayload({ commandType: types_2.CommandType.CMD_DOORBELL_SET_PAYLOAD, value: JSON.stringify({ "commandType": types_1.ParamType.COMMAND_LED_NIGHT_OPEN, "data": { "status": value === true ? 1 : 0 } }), channel: device.getChannel() }, { property: propertyData }); } else { throw new error_1.NotSupportedError(`This functionality is not implemented or supported by ${device.getSerial()}`); } } async setAutoNightVision(device, value) { const propertyData = { name: types_1.PropertyName.DeviceAutoNightvision, value: value }; if (device.getStationSerial() !== this.getSerial()) { throw new error_1.WrongStationError(`Device ${device.getSerial()} is not managed by this station ${this.getSerial()}`); } if (!device.hasProperty(propertyData.name)) { throw new error_1.NotSupportedError(`This functionality is not implemented or supported by ${device.getSerial()}`); } this.log.debug(`Sending autonightvision command to station ${this.getSerial()} for device ${device.getSerial()} with value: ${value}`); await this.p2pSession.sendCommandWithIntString({ commandType: types_2.CommandType.CMD_IRCUT_SWITCH, value: value === true ? 1 : 0, valueSub: device.getChannel(), channel: device.getChannel() }, { property: propertyData }); } async setNightVision(device, value) { const propertyData = { name: types_1.PropertyName.DeviceNightvision, value: value }; if (device.getStationSerial() !== this.getSerial()) { throw new error_1.WrongStationError(`Device ${device.getSerial()} is not managed by this station ${this.getSerial()}`); } if (!device.hasProperty(propertyData.name)) { throw new error_1.NotSupportedError(`This functionality is not implemented or supported by ${device.getSerial()}`); } this.log.debug(`Sending nightvision command to station ${this.getSerial()} for device ${device.getSerial()} with value: ${value}`); await this.p2pSession.sendCommandWithStringPayload({ commandType: types_2.CommandType.CMD_SET_PAYLOAD, value: JSON.stringify({ "account_id": this.rawStation.member.admin_user_id, "cmd": types_2.CommandType.CMD_SET_NIGHT_VISION_TYPE, "mValue3": 0, "payload": { "channel": device.getChannel(), "night_sion": value, } }), channel: device.getChannel() }, { property: propertyData }); } async setMotionDetection(device, value) { const propertyData = { name: types_1.PropertyName.DeviceMotionDetection, value: value }; if (device.getStationSerial() !== this.getSerial()) { throw new error_1.WrongStationError(`Device ${device.getSerial()} is not managed by this station ${this.getSerial()}`); } if (!device.hasProperty(propertyData.name)) { throw new error_1.NotSupportedError(`This functionality is not implemented or supported by ${device.getSerial()}`); } this.log.debug(`Sending motion detection command to station ${this.getSerial()} for device ${device.getSerial()} with value: ${value}`); if (device.isIndoorCamera() || (device.isFloodLight() && device.getDeviceType() !== types_1.DeviceType.FLOODLIGHT)) { await this.p2pSession.sendCommandWithStringPayload({ commandType: types_2.CommandType.CMD_DOORBELL_SET_PAYLOAD, value: JSON.stringify({ "commandType": types_2.CommandType.CMD_INDOOR_DET_SET_MOTION_DETECT_ENABLE, "data": { "enable": 0, "index": 0, "status": value === true ? 1 : 0, "type": 0, "value": 0, "voiceID": 0, "zonecount": 0 } }), channel: device.getChannel() }, { property: propertyData }); } else if (device.isSoloCameras()) { await this.p2pSession.sendCommandWithStringPayload({ commandType: types_2.CommandType.CMD_DOORBELL_SET_PAYLOAD, value: JSON.stringify({ "commandType": types_2.CommandType.CMD_INDOOR_DET_SET_MOTION_DETECT_ENABLE, "data": { "enable": 0, "index": 0, "status": value === true ? 1 : 0, "type": 0, "url": "", "value": 0, "voiceID": 0, "zonecount": 0 } }), channel: device.getChannel() }, { property: propertyData }); } else if (device.isWiredDoorbell()) { await this.p2pSession.sendCommandWithStringPayload({ commandType: types_2.CommandType.CMD_DOORBELL_SET_PAYLOAD, value: JSON.stringify({ "commandType": types_1.ParamType.COMMAND_MOTION_DETECTION_PACKAGE, "data": { "enable": value === true ? 1 : 0, } }), channel: device.getChannel() }, { property: propertyData }); } else { await this.p2pSession.sendCommandWithIntString({ commandType: types_2.CommandType.CMD_PIR_SWITCH, value: value === true ? 1 : 0, valueSub: device.getChannel(), strValue: this.rawStation.member.admin_user_id, channel: device.getChannel() }, { property: propertyData }); } } async setSoundDetection(device, value) { const propertyData = { name: types_1.PropertyName.DeviceSoundDetection, value: value }; if (device.getStationSerial() !== this.getSerial()) { throw new error_1.WrongStationError(`Device ${device.getSerial()} is not managed by this station ${this.getSerial()}`); } if (!device.hasProperty(propertyData.name)) { throw new error_1.NotSupportedError(`This functionality is not implemented or supported by ${device.getSerial()}`); } this.log.debug(`Sending sound detection command to station ${this.getSerial()} for device ${device.getSerial()} with value: ${value}`); await this.p2pSession.sendCommandWithStringPayload({ commandType: types_2.CommandType.CMD_DOORBELL_SET_PAYLOAD, value: JSON.stringify({ "commandType": types_2.CommandType.CMD_INDOOR_DET_SET_SOUND_DETECT_ENABLE, "data": { "enable": 0, "index": 0, "status": value === true ? 1 : 0, "type": 0, "value": 0, "voiceID": 0, "zonecount": 0 } }), channel: device.getChannel() }, { property: propertyData }); } async setSoundDetectionType(device, value) { const propertyData = { name: types_1.PropertyName.DeviceSoundDetectionType, value: value }; if (device.getStationSerial() !== this.getSerial()) { throw new error_1.WrongStationError(`Device ${device.getSerial()} is not managed by this station ${this.getSerial()}`); } if (!device.hasProperty(propertyData.name)) { throw new error_1.NotSupportedError(`This functionality is not implemented or supported by ${device.getSerial()}`); } const property = device.getPropertyMetadata(propertyData.name); (0, utils_3.validValue)(property, value); this.log.debug(`Sending sound detection type command to station ${this.getSerial()} for device ${device.getSerial()} with value: ${value}`); await this.p2pSession.sendCommandWithStringPayload({ commandType: types_2.CommandType.CMD_DOORBELL_SET_PAYLOAD, value: JSON.stringify({ "commandType": types_2.CommandType.CMD_INDOOR_DET_SET_SOUND_DETECT_TYPE, "data": { "enable": 0, "index": 0, "status": 0, "type": value, "value": 0, "voiceID": 0, "zonecount": 0 } }), channel: device.getChannel() }, { property: propertyData }); } async setSoundDetectionSensitivity(device, value) { const propertyData = { name: types_1.PropertyName.DeviceSoundDetectionSensitivity, value: value }; if (device.getStationSerial() !== this.getSerial()) { throw new error_1.WrongStationError(`Device ${device.getSerial()} is not managed by this station ${this.getSerial()}`); } if (!device.hasProperty(propertyData.name)) { throw new error_1.NotSupportedError(`This func