diff --git a/README.md b/README.md index a846603..5f4dd6d 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,12 @@ Best is to set the adapter to Debug log mode (Instances -> Expert mode -> Column ## Changelog +### 0.2.3 (2021-02-17) +* (bropat) Fixed wired doorbell p2p livestream (should fix also indoor, floodlight and solo cameras) +* (bropat) Fixed issue that treats known push notifications as unknown +* (bropat) Fixed relative path for state last_event_pic_url +* (bropat) Updated versions of the package dependencies + ### 0.2.2 (2021-02-16) * (bropat) Fixed web extension settings for serving videos and pictures (issue [#79](https://github.com/bropat/ioBroker.eufy-security/issues/78)) diff --git a/build/lib/eufy-security/eufy-security.js b/build/lib/eufy-security/eufy-security.js index dc715aa..9a8ce57 100644 --- a/build/lib/eufy-security/eufy-security.js +++ b/build/lib/eufy-security/eufy-security.js @@ -95,7 +95,7 @@ class EufySecurity extends tiny_typed_emitter_1.TypedEmitter { } getStationDevice(station_sn, channel) { for (const device of Object.values(this.devices)) { - if (device.getStationSerial() === station_sn && device.getChannel() === channel) { + if ((device.getStationSerial() === station_sn && device.getChannel() === channel) || (device.getStationSerial() === station_sn && device.getSerial() === station_sn)) { return device; } } @@ -423,7 +423,7 @@ class EufySecurity extends tiny_typed_emitter_1.TypedEmitter { try { const device = this.getStationDevice(station.getSerial(), channel); try { - yield utils_1.removeFiles(this.adapter.namespace, station.getSerial(), types_2.DataLocation.TEMP, device.getSerial()); + yield utils_1.removeFiles(this.adapter.namespace, station.getSerial(), types_2.DataLocation.TEMP, device.getSerial()).catch(); const file_path = utils_1.getDataFilePath(this.adapter.namespace, station.getSerial(), types_2.DataLocation.TEMP, `${device.getSerial()}${types_2.STREAM_FILE_NAME_EXT}`); video_1.ffmpegStreamToHls(this.adapter.namespace, metadata, videostream, audiostream, file_path, this.log) .then(() => { @@ -477,9 +477,8 @@ class EufySecurity extends tiny_typed_emitter_1.TypedEmitter { try { const device = this.getStationDevice(station.getSerial(), channel); try { - const device = this.getStationDevice(station.getSerial(), channel); const file_path = utils_1.getDataFilePath(this.adapter.namespace, station.getSerial(), types_2.DataLocation.LIVESTREAM, `${device.getSerial()}${types_2.STREAM_FILE_NAME_EXT}`); - yield utils_1.removeFiles(this.adapter.namespace, station.getSerial(), types_2.DataLocation.LIVESTREAM, device.getSerial()); + yield utils_1.removeFiles(this.adapter.namespace, station.getSerial(), types_2.DataLocation.LIVESTREAM, device.getSerial()).catch(); video_1.ffmpegStreamToHls(this.adapter.namespace, metadata, videostream, audiostream, file_path, this.log) .then(() => { return utils_1.removeFiles(this.adapter.namespace, station.getSerial(), types_2.DataLocation.LAST_LIVESTREAM, device.getSerial()); diff --git a/build/lib/eufy-security/utils.js b/build/lib/eufy-security/utils.js index 5de3b59..63d32af 100644 --- a/build/lib/eufy-security/utils.js +++ b/build/lib/eufy-security/utils.js @@ -31,7 +31,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.lowestUnusedNumber = exports.moveFiles = exports.removeFiles = exports.getDataFilePath = exports.saveImageStates = exports.setStateWithTimestamp = exports.setStateChangedWithTimestamp = exports.saveImage = exports.getImageAsHTML = exports.getImage = exports.getState = exports.isEmpty = exports.setStateChangedAsync = exports.md5 = exports.generateSerialnumber = exports.generateUDID = exports.decrypt = void 0; +exports.lowestUnusedNumber = exports.moveFiles = exports.removeFiles = exports.saveImageStates = exports.setStateWithTimestamp = exports.setStateChangedWithTimestamp = exports.saveImage = exports.getDataFilePath = exports.getImageAsHTML = exports.getImage = exports.getState = exports.isEmpty = exports.setStateChangedAsync = exports.md5 = exports.generateSerialnumber = exports.generateUDID = exports.decrypt = void 0; const crypto = __importStar(require("crypto")); const read_bigint_1 = require("read-bigint"); const axios_1 = __importDefault(require("axios")); @@ -114,7 +114,16 @@ const getImageAsHTML = function (data) { return ""; }; exports.getImageAsHTML = getImageAsHTML; -const saveImage = function (adapter, url, filename_without_extension) { +const getDataFilePath = function (namespace, stationSerial, folderName, fileName) { + const root_path = `${utils.getAbsoluteDefaultDataDir()}files${path_1.default.sep}${namespace}`; + const dir_path = `${root_path}${path_1.default.sep}${stationSerial}${path_1.default.sep}${folderName}`; + if (!fs_extra_1.default.existsSync(dir_path)) { + fs_extra_1.default.mkdirSync(dir_path, { mode: 0o775, recursive: true }); + } + return `${dir_path}${path_1.default.sep}${fileName}`; +}; +exports.getDataFilePath = getDataFilePath; +const saveImage = function (adapter, url, station_sn, device_sn, location) { return __awaiter(this, void 0, void 0, function* () { const result = { status: 0, @@ -128,10 +137,9 @@ const saveImage = function (adapter, url, filename_without_extension) { result.status = response.status; result.statusText = response.statusText; if (response.status === 200) { - const filename = `${filename_without_extension}.jpg`; const data = Buffer.from(response.data); - yield adapter.writeFileAsync(`${adapter.name}.${adapter.instance}`, filename, data).then(() => { - result.imageUrl = `/${adapter.name}.${adapter.instance}/${filename}`; + yield adapter.writeFileAsync(adapter.namespace, `${station_sn}/${location}/${device_sn}${types_1.IMAGE_FILE_JPEG_EXT}`, data).then(() => { + result.imageUrl = `/${adapter.namespace}/${station_sn}/${location}/${device_sn}${types_1.IMAGE_FILE_JPEG_EXT}`; result.imageHtml = exports.getImageAsHTML(data); }).catch(error => { adapter.log.error(`saveImage(): writeFile Error: ${error} - url: ${url}`); @@ -189,14 +197,14 @@ const setStateWithTimestamp = function (adapter, state_id, common_name, value, t }); }; exports.setStateWithTimestamp = setStateWithTimestamp; -const saveImageStates = function (adapter, url, timestamp, serial_number, url_state_id, html_state_id, prefix_common_name, filename_prefix = "", retry = 1) { +const saveImageStates = function (adapter, url, timestamp, station_sn, device_sn, location, url_state_id, html_state_id, prefix_common_name, retry = 1) { return __awaiter(this, void 0, void 0, function* () { - const image_data = yield exports.saveImage(adapter, url, `${filename_prefix}${serial_number}`); + const image_data = yield exports.saveImage(adapter, url, station_sn, device_sn, location); if (image_data.status === 404) { if (retry < 6) { adapter.log.info(`Retry get image in ${5 * retry} seconds from url: ${url} (retry_count: ${retry} error: ${image_data.statusText} message: ${image_data.statusText})...`); setTimeout(() => { - exports.saveImageStates(adapter, url, timestamp, serial_number, url_state_id, html_state_id, prefix_common_name, filename_prefix, ++retry); + exports.saveImageStates(adapter, url, timestamp, station_sn, device_sn, location, url_state_id, html_state_id, prefix_common_name, ++retry); }, 5 * 1000 * retry); } else { @@ -209,15 +217,6 @@ const saveImageStates = function (adapter, url, timestamp, serial_number, url_st }); }; exports.saveImageStates = saveImageStates; -const getDataFilePath = function (namespace, stationSerial, folderName, fileName) { - const root_path = `${utils.getAbsoluteDefaultDataDir()}files${path_1.default.sep}${namespace}`; - const dir_path = `${root_path}${path_1.default.sep}${stationSerial}${path_1.default.sep}${folderName}`; - if (!fs_extra_1.default.existsSync(dir_path)) { - fs_extra_1.default.mkdirSync(dir_path, { mode: 0o775, recursive: true }); - } - return `${dir_path}${path_1.default.sep}${fileName}`; -}; -exports.getDataFilePath = getDataFilePath; const removeFiles = function (namespace, stationSerial, folderName, device_sn) { return new Promise((resolve, reject) => { try { diff --git a/build/main.js b/build/main.js index 893084e..3be72d0 100644 --- a/build/main.js +++ b/build/main.js @@ -607,7 +607,7 @@ class EufySecurity extends utils.Adapter { }); yield utils_1.setStateChangedAsync(this, camera.getStateID(types_1.CameraStateID.MAC_ADDRESS), camera.getMACAddress()); // Last event picture - yield utils_1.saveImageStates(this, camera.getLastCameraImageURL(), camera.getLastCameraImageTimestamp(), camera.getSerial(), camera.getStateID(types_1.CameraStateID.LAST_EVENT_PICTURE_URL), camera.getStateID(types_1.CameraStateID.LAST_EVENT_PICTURE_HTML), "Last event picture").catch(() => { + yield utils_1.saveImageStates(this, camera.getLastCameraImageURL(), camera.getLastCameraImageTimestamp(), camera.getStationSerial(), camera.getSerial(), types_1.DataLocation.LAST_EVENT, camera.getStateID(types_1.CameraStateID.LAST_EVENT_PICTURE_URL), camera.getStateID(types_1.CameraStateID.LAST_EVENT_PICTURE_HTML), "Last event picture").catch(() => { this.log.error(`handleDevices(): State LAST_EVENT_PICTURE_URL of device ${camera.getSerial()} - saveImageStates(): url ${camera.getLastCameraImageURL()}`); }); //TODO: As soon as we release the p2p download of videos, unlock this @@ -1385,7 +1385,7 @@ class EufySecurity extends utils.Adapter { case eufy_security_client_1.DoorbellPushEvent.MOTION_DETECTION: if (!utils_1.isEmpty(push_msg.pic_url)) { yield this.setStateAsync(device.getStateID(types_1.DoorbellStateID.MOTION_DETECTED), { val: true, ack: true }); - yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getSerial(), device.getStateID(types_1.DoorbellStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.DoorbellStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getStationSerial(), device.getSerial(), types_1.DataLocation.LAST_EVENT, device.getStateID(types_1.DoorbellStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.DoorbellStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): DoorbellPushEvent.MOTION_DETECTION of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); if (this.motionDetected[device.getSerial()]) @@ -1408,7 +1408,7 @@ class EufySecurity extends utils.Adapter { case eufy_security_client_1.DoorbellPushEvent.FACE_DETECTION: if (!utils_1.isEmpty(push_msg.pic_url)) { yield this.setStateAsync(device.getStateID(types_1.DoorbellStateID.PERSON_DETECTED), { val: true, ack: true }); - yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getSerial(), device.getStateID(types_1.DoorbellStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.DoorbellStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getStationSerial(), device.getSerial(), types_1.DataLocation.LAST_EVENT, device.getStateID(types_1.DoorbellStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.DoorbellStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): DoorbellPushEvent.FACE_DETECTION of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); yield this.setStateAsync(device.getStateID(types_1.DoorbellStateID.LAST_PERSON_IDENTIFIED), { val: "Unknown", ack: true }); @@ -1454,7 +1454,7 @@ class EufySecurity extends utils.Adapter { case eufy_security_client_1.IndoorPushEvent.MOTION_DETECTION: if (!utils_1.isEmpty(push_msg.pic_url)) { yield this.setStateAsync(device.getStateID(types_1.IndoorCameraStateID.MOTION_DETECTED), { val: true, ack: true }); - yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getSerial(), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getStationSerial(), device.getSerial(), types_1.DataLocation.LAST_EVENT, device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): IndoorPushEvent.MOTION_DETECTION of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); if (this.motionDetected[device.getSerial()]) @@ -1477,7 +1477,7 @@ class EufySecurity extends utils.Adapter { case eufy_security_client_1.IndoorPushEvent.FACE_DETECTION: if (!utils_1.isEmpty(push_msg.pic_url)) { yield this.setStateAsync(device.getStateID(types_1.IndoorCameraStateID.PERSON_DETECTED), { val: true, ack: true }); - yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getSerial(), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getStationSerial(), device.getSerial(), types_1.DataLocation.LAST_EVENT, device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): IndoorPushEvent.FACE_DETECTION of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); yield this.setStateAsync(device.getStateID(types_1.IndoorCameraStateID.LAST_PERSON_IDENTIFIED), { val: "Unknown", ack: true }); @@ -1502,7 +1502,7 @@ class EufySecurity extends utils.Adapter { case eufy_security_client_1.IndoorPushEvent.CRYIG_DETECTION: if (!utils_1.isEmpty(push_msg.pic_url)) { yield this.setStateAsync(device.getStateID(types_1.IndoorCameraStateID.CRYING_DETECTED), { val: true, ack: true }); - yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getSerial(), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getStationSerial(), device.getSerial(), types_1.DataLocation.LAST_EVENT, device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): IndoorPushEvent.CRYIG_DETECTION of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); if (this.cryingDetected[device.getSerial()]) @@ -1525,7 +1525,7 @@ class EufySecurity extends utils.Adapter { case eufy_security_client_1.IndoorPushEvent.SOUND_DETECTION: if (!utils_1.isEmpty(push_msg.pic_url)) { yield this.setStateAsync(device.getStateID(types_1.IndoorCameraStateID.SOUND_DETECTED), { val: true, ack: true }); - yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getSerial(), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getStationSerial(), device.getSerial(), types_1.DataLocation.LAST_EVENT, device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): IndoorPushEvent.SOUND_DETECTION of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); if (this.soundDetected[device.getSerial()]) @@ -1548,7 +1548,7 @@ class EufySecurity extends utils.Adapter { case eufy_security_client_1.IndoorPushEvent.PET_DETECTION: if (!utils_1.isEmpty(push_msg.pic_url)) { yield this.setStateAsync(device.getStateID(types_1.IndoorCameraStateID.PET_DETECTED), { val: true, ack: true }); - yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getSerial(), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getStationSerial(), device.getSerial(), types_1.DataLocation.LAST_EVENT, device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): IndoorPushEvent.PET_DETECTION of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); if (this.petDetected[device.getSerial()]) @@ -1586,7 +1586,7 @@ class EufySecurity extends utils.Adapter { if (device) { if (push_msg.fetch_id) { if (!utils_1.isEmpty(push_msg.pic_url)) { - yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getSerial(), device.getStateID(types_1.CameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.CameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + yield utils_1.saveImageStates(this, push_msg.pic_url, push_msg.event_time, device.getStationSerial(), device.getSerial(), types_1.DataLocation.LAST_EVENT, device.getStateID(types_1.CameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(types_1.CameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): CusPushEvent.SECURITY of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); if (utils_1.isEmpty(push_msg.person_name)) { diff --git a/io-package.json b/io-package.json index dd10c00..2c7cd6d 100644 --- a/io-package.json +++ b/io-package.json @@ -1,8 +1,20 @@ { "common": { "name": "eufy-security", - "version": "0.2.2", + "version": "0.2.3", "news": { + "0.2.3": { + "en": "Fixed wired doorbell p2p livestream and push notifications", + "de": "Feste kabelgebundene Türklingel p2p Livestream- und Push-Benachrichtigungen behoben", + "ru": "Фиксированный проводной дверной звонок p2p livestream и push-уведомления", + "pt": "Correção da transmissão ao vivo P2P da campainha com fio e notificações push", + "nl": "Vaste bedrade deurbel p2p livestream en pushmeldingen", + "fr": "Sonnette filaire fixe p2p livestream et notifications push", + "it": "Risolto il problema con il livestream p2p del campanello cablato e le notifiche push", + "es": "Timbre fijo con cable p2p transmisión en vivo y notificaciones push", + "pl": "Naprawiono transmisję na żywo i powiadomienia push z dzwonka do drzwi przewodowego", + "zh-cn": "固定有线门铃p2p直播和推送通知" + }, "0.2.2": { "en": "Fixed web extension settings for serving videos and pictures", "de": "Die Einstellungen für die Web-Erweiterung zum Bereitstellen von Videos und Bildern wurden korrigiert", diff --git a/package-lock.json b/package-lock.json index eca439c..d8a6f88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "iobroker.eufy-security", - "version": "0.2.1", + "version": "0.2.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -430,20 +430,20 @@ "dev": true }, "@types/mocha": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.0.tgz", - "integrity": "sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.1.tgz", + "integrity": "sha512-NysN+bNqj6E0Hv4CTGWSlPzMW6vTKjDpOteycDkV4IWBsO+PU48JonrPzV9ODjiI2XrjmA05KInLgF5ivZ/YGQ==", "dev": true }, "@types/node": { - "version": "14.14.27", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.27.tgz", - "integrity": "sha512-Ecfmo4YDQPwuqTCl1yBxLV5ihKfRlkBmzUEDcfIRvDxOTGQEeikr317Ln7Gcv0tjA8dVgKI3rniqW2G1OyKDng==" + "version": "14.14.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.28.tgz", + "integrity": "sha512-lg55ArB+ZiHHbBBttLpzD07akz0QPrZgUODNakeC09i62dnrywr9mFErHuaPlB6I7z+sEbK+IYmplahvplCj2g==" }, "@types/node-rsa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/node-rsa/-/node-rsa-1.0.0.tgz", - "integrity": "sha512-9hTSXhGDKotpq5XCm+r7wMgt5gl6bGH6VS/sV9bsKda4JmJYyOCdTenSkTUh7zsOihm2oCm5o2ZdfpUIGYKLzQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/node-rsa/-/node-rsa-1.1.0.tgz", + "integrity": "sha512-MDFFz1Fra3Xb4S7ZoWHPyFqq8WxRSI5c1Y/UKq/mD9HUAzGw9fu0LSgSvCzrqndtNPPw3DQS3petBVgQtPGfMA==", "requires": { "@types/node": "*" } @@ -544,13 +544,13 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.0.tgz", - "integrity": "sha512-DJgdGZW+8CFUTz5C/dnn4ONcUm2h2T0itWD85Ob5/V27Ndie8hUoX5HKyGssvR8sUMkAIlUc/AMK67Lqa3kBIQ==", + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.15.1.tgz", + "integrity": "sha512-yW2epMYZSpNJXZy22Biu+fLdTG8Mn6b22kR3TqblVk50HGNV8Zya15WAXuQCr8tKw4Qf1BL4QtI6kv6PCkLoJw==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.15.0", - "@typescript-eslint/scope-manager": "4.15.0", + "@typescript-eslint/experimental-utils": "4.15.1", + "@typescript-eslint/scope-manager": "4.15.1", "debug": "^4.1.1", "functional-red-black-tree": "^1.0.1", "lodash": "^4.17.15", @@ -560,55 +560,55 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.0.tgz", - "integrity": "sha512-V4vaDWvxA2zgesg4KPgEGiomWEBpJXvY4ZX34Y3qxK8LUm5I87L+qGIOTd9tHZOARXNRt9pLbblSKiYBlGMawg==", + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.15.1.tgz", + "integrity": "sha512-9LQRmOzBRI1iOdJorr4jEnQhadxK4c9R2aEAsm7WE/7dq8wkKD1suaV0S/JucTL8QlYUPU1y2yjqg+aGC0IQBQ==", "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.15.0", - "@typescript-eslint/types": "4.15.0", - "@typescript-eslint/typescript-estree": "4.15.0", + "@typescript-eslint/scope-manager": "4.15.1", + "@typescript-eslint/types": "4.15.1", + "@typescript-eslint/typescript-estree": "4.15.1", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" } }, "@typescript-eslint/parser": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.15.0.tgz", - "integrity": "sha512-L6Dtbq8Bc7g2aZwnIBETpmUa9XDKCMzKVwAArnGp5Mn7PRNFjf3mUzq8UeBjL3K8t311hvevnyqXAMSmxO8Gpg==", + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.15.1.tgz", + "integrity": "sha512-V8eXYxNJ9QmXi5ETDguB7O9diAXlIyS+e3xzLoP/oVE4WCAjssxLIa0mqCLsCGXulYJUfT+GV70Jv1vHsdKwtA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.15.0", - "@typescript-eslint/types": "4.15.0", - "@typescript-eslint/typescript-estree": "4.15.0", + "@typescript-eslint/scope-manager": "4.15.1", + "@typescript-eslint/types": "4.15.1", + "@typescript-eslint/typescript-estree": "4.15.1", "debug": "^4.1.1" } }, "@typescript-eslint/scope-manager": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.0.tgz", - "integrity": "sha512-CSNBZnCC2jEA/a+pR9Ljh8Y+5TY5qgbPz7ICEk9WCpSEgT6Pi7H2RIjxfrrbUXvotd6ta+i27sssKEH8Azm75g==", + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.15.1.tgz", + "integrity": "sha512-ibQrTFcAm7yG4C1iwpIYK7vDnFg+fKaZVfvyOm3sNsGAerKfwPVFtYft5EbjzByDJ4dj1WD8/34REJfw/9wdVA==", "dev": true, "requires": { - "@typescript-eslint/types": "4.15.0", - "@typescript-eslint/visitor-keys": "4.15.0" + "@typescript-eslint/types": "4.15.1", + "@typescript-eslint/visitor-keys": "4.15.1" } }, "@typescript-eslint/types": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.0.tgz", - "integrity": "sha512-su4RHkJhS+iFwyqyXHcS8EGPlUVoC+XREfy5daivjLur9JP8GhvTmDipuRpcujtGC4M+GYhUOJCPDE3rC5NJrg==", + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.15.1.tgz", + "integrity": "sha512-iGsaUyWFyLz0mHfXhX4zO6P7O3sExQpBJ2dgXB0G5g/8PRVfBBsmQIc3r83ranEQTALLR3Vko/fnCIVqmH+mPw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.0.tgz", - "integrity": "sha512-jG6xTmcNbi6xzZq0SdWh7wQ9cMb2pqXaUp6bUZOMsIlu5aOlxGxgE/t6L/gPybybQGvdguajXGkZKSndZJpksA==", + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.15.1.tgz", + "integrity": "sha512-z8MN3CicTEumrWAEB2e2CcoZa3KP9+SMYLIA2aM49XW3cWIaiVSOAGq30ffR5XHxRirqE90fgLw3e6WmNx5uNw==", "dev": true, "requires": { - "@typescript-eslint/types": "4.15.0", - "@typescript-eslint/visitor-keys": "4.15.0", + "@typescript-eslint/types": "4.15.1", + "@typescript-eslint/visitor-keys": "4.15.1", "debug": "^4.1.1", "globby": "^11.0.1", "is-glob": "^4.0.1", @@ -617,12 +617,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.0.tgz", - "integrity": "sha512-RnDtJwOwFucWFAMjG3ghCG/ikImFJFEg20DI7mn4pHEx3vC48lIAoyjhffvfHmErRDboUPC7p9Z2il4CLb7qxA==", + "version": "4.15.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.15.1.tgz", + "integrity": "sha512-tYzaTP9plooRJY8eNlpAewTOqtWW/4ff/5wBjNVaJ0S0wC4Gpq/zDVRTJa5bq2v1pCNQ08xxMCndcvR+h7lMww==", "dev": true, "requires": { - "@typescript-eslint/types": "4.15.0", + "@typescript-eslint/types": "4.15.1", "eslint-visitor-keys": "^2.0.0" } }, @@ -1914,12 +1914,12 @@ "dev": true }, "eufy-security-client": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/eufy-security-client/-/eufy-security-client-0.4.2.tgz", - "integrity": "sha512-uCeZJ0q17B3qVYXzib8Gi+Ty6SM/e/LmSfZQmDLWuxyxFsjZzaj9Kxicga1E/IWjVHfNNFcpzpxB2PdOhQ7yUg==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/eufy-security-client/-/eufy-security-client-0.4.3.tgz", + "integrity": "sha512-R5MLqLychEssOPHh4r+oFgaJ7LMddCFv86BRrur4+8rv0CHcVVEnmVnQUIg5wRViJGkkPkpZb0S7/NYL6WAdkw==", "requires": { "@types/crypto-js": "^4.0.1", - "@types/node-rsa": "^1.0.0", + "@types/node-rsa": "^1.1.0", "@types/qs": "^6.9.5", "axios": "^0.21.1", "crypto-js": "^4.0.0", @@ -3742,9 +3742,9 @@ } }, "mime": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.0.tgz", - "integrity": "sha512-ft3WayFSFUVBuJj7BMLKAQcSlItKtfjsKDDsii3rqFDAZ7t11zRe8ASw/GlmivGwVUYtwkQrxiGGpL6gFvB0ag==" + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz", + "integrity": "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==" }, "minimatch": { "version": "3.0.4", diff --git a/package.json b/package.json index b31bcdf..cab9c5b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iobroker.eufy-security", - "version": "0.2.2", + "version": "0.2.3", "description": "ioBroker adapter that integrates Eufy-Security cameras with stations", "author": { "name": "bropat", @@ -24,7 +24,7 @@ }, "dependencies": { "@iobroker/adapter-core": "^2.4.0", - "eufy-security-client": "^0.4.2", + "eufy-security-client": "^0.4.3", "axios": "^0.21.1", "read-bigint": "^0.1.4", "@types/ffmpeg-static": "^3.0.0", @@ -32,7 +32,7 @@ "ffmpeg-static": "^4.2.7", "fluent-ffmpeg": "^2.1.2", "@types/express": "^4.17.11", - "mime": "^2.5.0", + "mime": "^2.5.2", "@types/mime": "^2.0.3", "fs-extra": "^9.1.0", "@types/fs-extra": "^9.0.7", @@ -43,13 +43,13 @@ "@types/chai": "^4.2.15", "@types/chai-as-promised": "^7.1.3", "@types/gulp": "^4.0.8", - "@types/mocha": "^8.2.0", - "@types/node": "^14.14.27", + "@types/mocha": "^8.2.1", + "@types/node": "^14.14.28", "@types/proxyquire": "^1.3.28", "@types/sinon": "^9.0.10", "@types/sinon-chai": "^3.2.5", - "@typescript-eslint/eslint-plugin": "^4.15.0", - "@typescript-eslint/parser": "^4.15.0", + "@typescript-eslint/eslint-plugin": "^4.15.1", + "@typescript-eslint/parser": "^4.15.1", "chai": "^4.3.0", "chai-as-promised": "^7.1.1", "eslint": "^7.20.0", diff --git a/src/lib/eufy-security/eufy-security.ts b/src/lib/eufy-security/eufy-security.ts index 796a8cd..218461e 100644 --- a/src/lib/eufy-security/eufy-security.ts +++ b/src/lib/eufy-security/eufy-security.ts @@ -107,7 +107,7 @@ export class EufySecurity extends TypedEmitter { public getStationDevice(station_sn: string, channel: number): Device { for (const device of Object.values(this.devices)) { - if (device.getStationSerial() === station_sn && device.getChannel() === channel) { + if ((device.getStationSerial() === station_sn && device.getChannel() === channel) || (device.getStationSerial() === station_sn && device.getSerial() === station_sn)) { return device; } } @@ -416,7 +416,7 @@ export class EufySecurity extends TypedEmitter { try { const device = this.getStationDevice(station.getSerial(), channel); try { - await removeFiles(this.adapter.namespace, station.getSerial(), DataLocation.TEMP, device.getSerial()); + await removeFiles(this.adapter.namespace, station.getSerial(), DataLocation.TEMP, device.getSerial()).catch(); const file_path = getDataFilePath(this.adapter.namespace, station.getSerial(), DataLocation.TEMP, `${device.getSerial()}${STREAM_FILE_NAME_EXT}`); ffmpegStreamToHls(this.adapter.namespace, metadata, videostream, audiostream, file_path, this.log) @@ -467,9 +467,8 @@ export class EufySecurity extends TypedEmitter { try { const device = this.getStationDevice(station.getSerial(), channel); try { - const device = this.getStationDevice(station.getSerial(), channel); const file_path = getDataFilePath(this.adapter.namespace, station.getSerial(), DataLocation.LIVESTREAM, `${device.getSerial()}${STREAM_FILE_NAME_EXT}`); - await removeFiles(this.adapter.namespace, station.getSerial(), DataLocation.LIVESTREAM, device.getSerial()); + await removeFiles(this.adapter.namespace, station.getSerial(), DataLocation.LIVESTREAM, device.getSerial()).catch(); ffmpegStreamToHls(this.adapter.namespace, metadata, videostream, audiostream, file_path, this.log) .then(() => { return removeFiles(this.adapter.namespace, station.getSerial(), DataLocation.LAST_LIVESTREAM, device.getSerial()); diff --git a/src/lib/eufy-security/utils.ts b/src/lib/eufy-security/utils.ts index d8b9430..57022ca 100644 --- a/src/lib/eufy-security/utils.ts +++ b/src/lib/eufy-security/utils.ts @@ -6,7 +6,7 @@ import path from "path"; import fse from "fs-extra"; import * as utils from "@iobroker/adapter-core"; -import { CameraStateID, StationStateID } from "./types"; +import { CameraStateID, IMAGE_FILE_JPEG_EXT, StationStateID } from "./types"; import { ImageResponse } from "./interfaces"; export const decrypt = (key: string, value: string): string => { @@ -80,7 +80,16 @@ export const getImageAsHTML = function(data: Buffer): string { return ""; } -export const saveImage = async function(adapter: ioBroker.Adapter, url: string, filename_without_extension: string): Promise { +export const getDataFilePath = function(namespace: string, stationSerial: string, folderName: string, fileName: string): string { + const root_path = `${utils.getAbsoluteDefaultDataDir()}files${path.sep}${namespace}` + const dir_path = `${root_path}${path.sep}${stationSerial}${path.sep}${folderName}`; + if (!fse.existsSync(dir_path)) { + fse.mkdirSync(dir_path, {mode: 0o775, recursive: true}); + } + return `${dir_path}${path.sep}${fileName}`; +} + +export const saveImage = async function(adapter: ioBroker.Adapter, url: string, station_sn: string, device_sn: string, location: string): Promise { const result: ImageResponse = { status: 0, statusText: "", @@ -93,10 +102,9 @@ export const saveImage = async function(adapter: ioBroker.Adapter, url: string, result.status = response.status; result.statusText = response.statusText; if (response.status === 200) { - const filename = `${filename_without_extension}.jpg`; const data = Buffer.from(response.data); - await adapter.writeFileAsync(`${adapter.name}.${adapter.instance}`, filename, data).then(() => { - result.imageUrl = `/${adapter.name}.${adapter.instance}/${filename}`; + await adapter.writeFileAsync(adapter.namespace, `${station_sn}/${location}/${device_sn}${IMAGE_FILE_JPEG_EXT}`, data).then(() => { + result.imageUrl = `/${adapter.namespace}/${station_sn}/${location}/${device_sn}${IMAGE_FILE_JPEG_EXT}`; result.imageHtml = getImageAsHTML(data); }).catch(error => { adapter.log.error(`saveImage(): writeFile Error: ${error} - url: ${url}`); @@ -149,13 +157,13 @@ export const setStateWithTimestamp = async function(adapter: ioBroker.Adapter, s } } -export const saveImageStates = async function(adapter: ioBroker.Adapter, url: string, timestamp:number, serial_number: string, url_state_id: string, html_state_id: string, prefix_common_name: string, filename_prefix = "", retry = 1): Promise { - const image_data = await saveImage(adapter, url, `${filename_prefix}${serial_number}`); +export const saveImageStates = async function(adapter: ioBroker.Adapter, url: string, timestamp:number, station_sn: string, device_sn: string, location: string, url_state_id: string, html_state_id: string, prefix_common_name: string, retry = 1): Promise { + const image_data = await saveImage(adapter, url, station_sn, device_sn, location); if (image_data.status === 404) { if (retry < 6) { adapter.log.info(`Retry get image in ${5 * retry} seconds from url: ${url} (retry_count: ${retry} error: ${image_data.statusText} message: ${image_data.statusText})...`); setTimeout(() => { - saveImageStates(adapter, url, timestamp, serial_number, url_state_id, html_state_id, prefix_common_name, filename_prefix, ++retry); + saveImageStates(adapter, url, timestamp, station_sn, device_sn, location, url_state_id, html_state_id, prefix_common_name, ++retry); }, 5 * 1000 * retry); } else { adapter.log.warn(`Could not download the image within 5 attempts from url: ${url} (error: ${image_data.statusText} message: ${image_data.statusText})`); @@ -166,15 +174,6 @@ export const saveImageStates = async function(adapter: ioBroker.Adapter, url: st setStateWithTimestamp(adapter, html_state_id, `${prefix_common_name} HTML image`, image_data.imageHtml, timestamp); } -export const getDataFilePath = function(namespace: string, stationSerial: string, folderName: string, fileName: string): string { - const root_path = `${utils.getAbsoluteDefaultDataDir()}files${path.sep}${namespace}` - const dir_path = `${root_path}${path.sep}${stationSerial}${path.sep}${folderName}`; - if (!fse.existsSync(dir_path)) { - fse.mkdirSync(dir_path, {mode: 0o775, recursive: true}); - } - return `${dir_path}${path.sep}${fileName}`; -} - export const removeFiles = function(namespace: string, stationSerial: string, folderName: string, device_sn: string): Promise { return new Promise((resolve, reject) => { try { diff --git a/src/main.ts b/src/main.ts index 17d9708..af852e7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,7 +11,7 @@ import { Camera, Device, EntrySensor, Keypad, MotionSensor, Devices, Station, St import * as EufySecurityAPI from "./lib/eufy-security/eufy-security"; import * as Interface from "./lib/eufy-security/interfaces" -import { CameraStateID, DeviceStateID, DoorbellStateID, EntrySensorStateID, IndoorCameraStateID, KeyPadStateID, MotionSensorStateID, StationStateID } from "./lib/eufy-security/types"; +import { CameraStateID, DataLocation, DeviceStateID, DoorbellStateID, EntrySensorStateID, IndoorCameraStateID, KeyPadStateID, MotionSensorStateID, StationStateID } from "./lib/eufy-security/types"; import { decrypt, generateSerialnumber, generateUDID, isEmpty, md5, saveImageStates, setStateChangedAsync, setStateChangedWithTimestamp } from "./lib/eufy-security/utils"; import { PersistentData } from "./lib/eufy-security/interfaces"; @@ -643,7 +643,7 @@ export class EufySecurity extends utils.Adapter { await setStateChangedAsync(this, camera.getStateID(CameraStateID.MAC_ADDRESS), camera.getMACAddress()); // Last event picture - await saveImageStates(this, camera.getLastCameraImageURL(), camera.getLastCameraImageTimestamp(), camera.getSerial(), camera.getStateID(CameraStateID.LAST_EVENT_PICTURE_URL),camera.getStateID(CameraStateID.LAST_EVENT_PICTURE_HTML), "Last event picture").catch(() => { + await saveImageStates(this, camera.getLastCameraImageURL(), camera.getLastCameraImageTimestamp(), camera.getStationSerial(), camera.getSerial(), DataLocation.LAST_EVENT, camera.getStateID(CameraStateID.LAST_EVENT_PICTURE_URL),camera.getStateID(CameraStateID.LAST_EVENT_PICTURE_HTML), "Last event picture").catch(() => { this.log.error(`handleDevices(): State LAST_EVENT_PICTURE_URL of device ${camera.getSerial()} - saveImageStates(): url ${camera.getLastCameraImageURL()}`); }); @@ -1451,7 +1451,7 @@ export class EufySecurity extends utils.Adapter { case DoorbellPushEvent.MOTION_DETECTION: if (!isEmpty(push_msg.pic_url)) { await this.setStateAsync(device.getStateID(DoorbellStateID.MOTION_DETECTED), { val: true, ack: true }); - await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getSerial(), device.getStateID(DoorbellStateID.LAST_EVENT_PICTURE_URL), device.getStateID(DoorbellStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getStationSerial(), device.getSerial(), DataLocation.LAST_EVENT, device.getStateID(DoorbellStateID.LAST_EVENT_PICTURE_URL), device.getStateID(DoorbellStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): DoorbellPushEvent.MOTION_DETECTION of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); @@ -1474,7 +1474,7 @@ export class EufySecurity extends utils.Adapter { case DoorbellPushEvent.FACE_DETECTION: if (!isEmpty(push_msg.pic_url)) { await this.setStateAsync(device.getStateID(DoorbellStateID.PERSON_DETECTED), { val: true, ack: true }); - await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getSerial(), device.getStateID(DoorbellStateID.LAST_EVENT_PICTURE_URL), device.getStateID(DoorbellStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getStationSerial(), device.getSerial(), DataLocation.LAST_EVENT, device.getStateID(DoorbellStateID.LAST_EVENT_PICTURE_URL), device.getStateID(DoorbellStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): DoorbellPushEvent.FACE_DETECTION of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); @@ -1519,7 +1519,7 @@ export class EufySecurity extends utils.Adapter { case IndoorPushEvent.MOTION_DETECTION: if (!isEmpty(push_msg.pic_url)) { await this.setStateAsync(device.getStateID(IndoorCameraStateID.MOTION_DETECTED), { val: true, ack: true }); - await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getSerial(), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getStationSerial(), device.getSerial(), DataLocation.LAST_EVENT, device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): IndoorPushEvent.MOTION_DETECTION of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); if (this.motionDetected[device.getSerial()]) @@ -1541,7 +1541,7 @@ export class EufySecurity extends utils.Adapter { case IndoorPushEvent.FACE_DETECTION: if (!isEmpty(push_msg.pic_url)) { await this.setStateAsync(device.getStateID(IndoorCameraStateID.PERSON_DETECTED), { val: true, ack: true }); - await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getSerial(), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getStationSerial(), device.getSerial(), DataLocation.LAST_EVENT, device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): IndoorPushEvent.FACE_DETECTION of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); await this.setStateAsync(device.getStateID(IndoorCameraStateID.LAST_PERSON_IDENTIFIED), { val: "Unknown", ack: true }); @@ -1565,7 +1565,7 @@ export class EufySecurity extends utils.Adapter { case IndoorPushEvent.CRYIG_DETECTION: if (!isEmpty(push_msg.pic_url)) { await this.setStateAsync(device.getStateID(IndoorCameraStateID.CRYING_DETECTED), { val: true, ack: true }); - await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getSerial(), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getStationSerial(), device.getSerial(), DataLocation.LAST_EVENT, device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): IndoorPushEvent.CRYIG_DETECTION of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); if (this.cryingDetected[device.getSerial()]) @@ -1587,7 +1587,7 @@ export class EufySecurity extends utils.Adapter { case IndoorPushEvent.SOUND_DETECTION: if (!isEmpty(push_msg.pic_url)) { await this.setStateAsync(device.getStateID(IndoorCameraStateID.SOUND_DETECTED), { val: true, ack: true }); - await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getSerial(), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getStationSerial(), device.getSerial(), DataLocation.LAST_EVENT, device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): IndoorPushEvent.SOUND_DETECTION of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); if (this.soundDetected[device.getSerial()]) @@ -1609,7 +1609,7 @@ export class EufySecurity extends utils.Adapter { case IndoorPushEvent.PET_DETECTION: if (!isEmpty(push_msg.pic_url)) { await this.setStateAsync(device.getStateID(IndoorCameraStateID.PET_DETECTED), { val: true, ack: true }); - await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getSerial(), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getStationSerial(), device.getSerial(), DataLocation.LAST_EVENT, device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(IndoorCameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): IndoorPushEvent.PET_DETECTION of device ${device.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); if (this.petDetected[device.getSerial()]) @@ -1645,7 +1645,7 @@ export class EufySecurity extends utils.Adapter { if (device) { if (push_msg.fetch_id) { if (!isEmpty(push_msg.pic_url)) { - await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getSerial(), device.getStateID(CameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(CameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture", "last_captured_").catch(() => { + await saveImageStates(this, push_msg.pic_url!, push_msg.event_time, device.getStationSerial(), device.getSerial(), DataLocation.LAST_EVENT, device.getStateID(CameraStateID.LAST_EVENT_PICTURE_URL), device.getStateID(CameraStateID.LAST_EVENT_PICTURE_HTML), "Last captured picture").catch(() => { this.log.error(`handlePushNotifications(): CusPushEvent.SECURITY of device ${device!.getSerial()} - saveImageStates(): url ${push_msg.pic_url}`); }); if (isEmpty(push_msg.person_name)) {