homebridge-eufy-security 4.4.2-beta.2 → 4.4.2-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/FUNDING.yml +1 -0
- package/README.md +14 -17
- package/dist/accessories/AutoSyncStationAccessory.js +26 -30
- package/dist/accessories/AutoSyncStationAccessory.js.map +1 -1
- package/dist/accessories/BaseAccessory.js +33 -46
- package/dist/accessories/BaseAccessory.js.map +1 -1
- package/dist/accessories/CameraAccessory.js +92 -167
- package/dist/accessories/CameraAccessory.js.map +1 -1
- package/dist/accessories/Device.js +11 -15
- package/dist/accessories/Device.js.map +1 -1
- package/dist/accessories/EntrySensorAccessory.js +7 -12
- package/dist/accessories/EntrySensorAccessory.js.map +1 -1
- package/dist/accessories/LockAccessory.js +20 -24
- package/dist/accessories/LockAccessory.js.map +1 -1
- package/dist/accessories/MotionSensorAccessory.js +7 -12
- package/dist/accessories/MotionSensorAccessory.js.map +1 -1
- package/dist/accessories/StationAccessory.js +40 -45
- package/dist/accessories/StationAccessory.js.map +1 -1
- package/dist/config.js +1 -4
- package/dist/config.js.map +1 -1
- package/dist/controller/LocalLivestreamManager.js +85 -89
- package/dist/controller/LocalLivestreamManager.js.map +1 -1
- package/dist/controller/recordingDelegate.js +116 -108
- package/dist/controller/recordingDelegate.js.map +1 -1
- package/dist/controller/snapshotDelegate.js +379 -0
- package/dist/controller/snapshotDelegate.js.map +1 -0
- package/dist/controller/streamingDelegate.js +202 -207
- package/dist/controller/streamingDelegate.js.map +1 -1
- package/dist/index.js +9 -7
- package/dist/index.js.map +1 -1
- package/dist/interfaces.js +1 -2
- package/dist/interfaces.js.map +1 -1
- package/dist/platform.js +145 -165
- package/dist/platform.js.map +1 -1
- package/dist/settings.js +26 -19
- package/dist/settings.js.map +1 -1
- package/dist/utils/Talkback.js +9 -13
- package/dist/utils/Talkback.js.map +1 -1
- package/dist/utils/configTypes.js +12 -7
- package/dist/utils/configTypes.js.map +1 -1
- package/dist/utils/ffmpeg.js +44 -52
- package/dist/utils/ffmpeg.js.map +1 -1
- package/dist/utils/interfaces.js +1 -5
- package/dist/utils/interfaces.js.map +1 -1
- package/dist/utils/utils.js +32 -134
- package/dist/utils/utils.js.map +1 -1
- package/dist/version.js +1 -4
- package/dist/version.js.map +1 -1
- package/eslint.config.mjs +25 -47
- package/homebridge-eufy-security.png +0 -0
- package/homebridge-ui/public/app.js +221 -0
- package/homebridge-ui/public/assets/devices/eufycam3pro_large.png +0 -0
- package/homebridge-ui/public/assets/devices/homebase3_large.png +0 -0
- package/homebridge-ui/public/assets/devices/homebasemini_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/indoorcamC210_large.png +0 -0
- package/homebridge-ui/public/assets/devices/indoorcamC220_large.png +0 -0
- package/homebridge-ui/public/assets/devices/indoorcamE30_large.png +0 -0
- package/homebridge-ui/public/assets/devices/solocamc35_large.png +0 -0
- package/homebridge-ui/public/assets/devices/solocame30_large.png +0 -0
- package/homebridge-ui/public/components/device-card.js +141 -0
- package/homebridge-ui/public/components/guard-modes.js +88 -0
- package/homebridge-ui/public/components/number-input.js +121 -0
- package/homebridge-ui/public/components/select.js +73 -0
- package/homebridge-ui/public/components/toggle.js +68 -0
- package/homebridge-ui/public/index.html +24 -14
- package/homebridge-ui/public/services/api.js +129 -0
- package/homebridge-ui/public/services/config.js +144 -0
- package/homebridge-ui/public/style.css +705 -0
- package/homebridge-ui/public/utils/countries.js +73 -0
- package/homebridge-ui/public/utils/device-images.js +69 -0
- package/homebridge-ui/public/utils/helpers.js +47 -0
- package/homebridge-ui/public/views/dashboard.js +211 -0
- package/homebridge-ui/public/views/device-detail.js +631 -0
- package/homebridge-ui/public/views/login.js +625 -0
- package/homebridge-ui/public/views/settings.js +389 -0
- package/homebridge-ui/public/views/unsupported-detail.js +216 -0
- package/homebridge-ui/server.js +567 -450
- package/package.json +19 -59
- package/dist/accessories/AutoSyncStationAccessory.d.ts +0 -42
- package/dist/accessories/AutoSyncStationAccessory.d.ts.map +0 -1
- package/dist/accessories/BaseAccessory.d.ts +0 -56
- package/dist/accessories/BaseAccessory.d.ts.map +0 -1
- package/dist/accessories/CameraAccessory.d.ts +0 -84
- package/dist/accessories/CameraAccessory.d.ts.map +0 -1
- package/dist/accessories/Device.d.ts +0 -19
- package/dist/accessories/Device.d.ts.map +0 -1
- package/dist/accessories/EntrySensorAccessory.d.ts +0 -25
- package/dist/accessories/EntrySensorAccessory.d.ts.map +0 -1
- package/dist/accessories/LockAccessory.d.ts +0 -45
- package/dist/accessories/LockAccessory.d.ts.map +0 -1
- package/dist/accessories/MotionSensorAccessory.d.ts +0 -25
- package/dist/accessories/MotionSensorAccessory.d.ts.map +0 -1
- package/dist/accessories/StationAccessory.d.ts +0 -86
- package/dist/accessories/StationAccessory.d.ts.map +0 -1
- package/dist/config.d.ts +0 -31
- package/dist/config.d.ts.map +0 -1
- package/dist/controller/LocalLivestreamManager.d.ts +0 -31
- package/dist/controller/LocalLivestreamManager.d.ts.map +0 -1
- package/dist/controller/SnapshotManager.d.ts +0 -64
- package/dist/controller/SnapshotManager.d.ts.map +0 -1
- package/dist/controller/SnapshotManager.js +0 -434
- package/dist/controller/SnapshotManager.js.map +0 -1
- package/dist/controller/recordingDelegate.d.ts +0 -27
- package/dist/controller/recordingDelegate.d.ts.map +0 -1
- package/dist/controller/streamingDelegate.d.ts +0 -52
- package/dist/controller/streamingDelegate.d.ts.map +0 -1
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/interfaces.d.ts +0 -15
- package/dist/interfaces.d.ts.map +0 -1
- package/dist/platform.d.ts +0 -94
- package/dist/platform.d.ts.map +0 -1
- package/dist/settings.d.ts +0 -23
- package/dist/settings.d.ts.map +0 -1
- package/dist/utils/Talkback.d.ts +0 -21
- package/dist/utils/Talkback.d.ts.map +0 -1
- package/dist/utils/configTypes.d.ts +0 -68
- package/dist/utils/configTypes.d.ts.map +0 -1
- package/dist/utils/ffmpeg.d.ts +0 -111
- package/dist/utils/ffmpeg.d.ts.map +0 -1
- package/dist/utils/interfaces.d.ts +0 -8
- package/dist/utils/interfaces.d.ts.map +0 -1
- package/dist/utils/utils.d.ts +0 -35
- package/dist/utils/utils.d.ts.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/homebridge-ui/configui/app/util/types.d.ts +0 -65
- package/homebridge-ui/configui/app/util/types.d.ts.map +0 -1
- package/homebridge-ui/configui/app/util/types.js +0 -18
- package/homebridge-ui/configui/app/util/types.js.map +0 -1
- package/homebridge-ui/public/3rdpartylicenses.txt +0 -561
- package/homebridge-ui/public/assets/devices/homebase2_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/homebase3_large.jpg +0 -0
- package/homebridge-ui/public/assets/images/homebridge-eufy-security.png +0 -0
- package/homebridge-ui/public/favicon.ico +0 -0
- package/homebridge-ui/public/main.f6423287e0411c87.js +0 -1
- package/homebridge-ui/public/polyfills.efa2f09ba3e49167.js +0 -1
- package/homebridge-ui/public/runtime.ccde331d62c423b3.js +0 -1
- package/homebridge-ui/public/styles.13e635bf7a488639.css +0 -5
- package/homebridge-ui/server.d.ts +0 -2
- package/homebridge-ui/server.d.ts.map +0 -1
- package/homebridge-ui/server.js.map +0 -1
- package/homebridge-ui/version.d.ts +0 -2
- package/homebridge-ui/version.d.ts.map +0 -1
- package/homebridge-ui/version.js +0 -5
- package/homebridge-ui/version.js.map +0 -1
package/dist/platform.js
CHANGED
|
@@ -1,26 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const node_os_1 = __importDefault(require("node:os"));
|
|
19
|
-
const node_process_1 = require("node:process");
|
|
20
|
-
const node_fs_1 = require("node:fs");
|
|
21
|
-
const utils_1 = require("./utils/utils");
|
|
22
|
-
const version_1 = require("./version");
|
|
23
|
-
class EufySecurityPlatform {
|
|
1
|
+
import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js';
|
|
2
|
+
import { DEFAULT_CONFIG_VALUES } from './config.js';
|
|
3
|
+
import { StationAccessory } from './accessories/StationAccessory.js';
|
|
4
|
+
import { EntrySensorAccessory } from './accessories/EntrySensorAccessory.js';
|
|
5
|
+
import { MotionSensorAccessory } from './accessories/MotionSensorAccessory.js';
|
|
6
|
+
import { CameraAccessory } from './accessories/CameraAccessory.js';
|
|
7
|
+
import { LockAccessory } from './accessories/LockAccessory.js';
|
|
8
|
+
import { AutoSyncStationAccessory } from './accessories/AutoSyncStationAccessory.js';
|
|
9
|
+
import { EufySecurity, Device, DeviceType, UserType, libVersion, LogLevel, } from 'eufy-security-client';
|
|
10
|
+
import { createStream } from 'rotating-file-stream';
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import os from 'node:os';
|
|
13
|
+
import { platform } from 'node:process';
|
|
14
|
+
import { readFileSync } from 'node:fs';
|
|
15
|
+
import { initLog, log, tsLogger, ffmpegLogger, HAP } from './utils/utils.js';
|
|
16
|
+
import { LIB_VERSION } from './version.js';
|
|
17
|
+
export class EufySecurityPlatform {
|
|
24
18
|
config;
|
|
25
19
|
api;
|
|
26
20
|
eufyClient = {};
|
|
@@ -37,8 +31,8 @@ class EufySecurityPlatform {
|
|
|
37
31
|
this.config = config;
|
|
38
32
|
this.api = api;
|
|
39
33
|
this.eufyPath = this.api.user.storagePath() + '/eufysecurity';
|
|
40
|
-
if (!
|
|
41
|
-
|
|
34
|
+
if (!fs.existsSync(this.eufyPath)) {
|
|
35
|
+
fs.mkdirSync(this.eufyPath);
|
|
42
36
|
}
|
|
43
37
|
// Identify what we're running on so we can take advantage of hardware-specific features.
|
|
44
38
|
this.probeHwOs();
|
|
@@ -56,10 +50,10 @@ class EufySecurityPlatform {
|
|
|
56
50
|
// Assigns the provided config object to this.config, casting it to the EufySecurityPlatformConfig type.
|
|
57
51
|
this.config = config;
|
|
58
52
|
// Iterates over each key in the DEFAULT_CONFIG_VALUES object.
|
|
59
|
-
Object.keys(
|
|
53
|
+
Object.keys(DEFAULT_CONFIG_VALUES).forEach(key => {
|
|
60
54
|
// Checks if the corresponding property in the config object is undefined or null.
|
|
61
55
|
// If it is, assigns the default value from DEFAULT_CONFIG_VALUES to it.
|
|
62
|
-
this.config[key] ??=
|
|
56
|
+
this.config[key] ??= DEFAULT_CONFIG_VALUES[key];
|
|
63
57
|
});
|
|
64
58
|
// List of properties that need to be parsed as numeric values
|
|
65
59
|
const numericProperties = [
|
|
@@ -139,12 +133,12 @@ class EufySecurityPlatform {
|
|
|
139
133
|
};
|
|
140
134
|
// Modify log options if detailed logging is enabled
|
|
141
135
|
if (this.config.enableDetailedLogging) {
|
|
142
|
-
logOptions.name = `[EufySecurity-${
|
|
136
|
+
logOptions.name = `[EufySecurity-${LIB_VERSION}]`; // Modify logger name with plugin version
|
|
143
137
|
logOptions.prettyLogTemplate = '[{{mm}}/{{dd}}/{{yyyy}} {{hh}}:{{MM}}:{{ss}}]\t{{name}}\t{{logLevelName}}\t[{{fileNameWithLine}}]\t'; // Modify log template
|
|
144
138
|
logOptions.minLevel = 2; // Adjust minimum log level
|
|
145
139
|
}
|
|
146
140
|
// Initialize the global logger with the configured options
|
|
147
|
-
(
|
|
141
|
+
initLog(logOptions);
|
|
148
142
|
// Configures log streams for various log files
|
|
149
143
|
this.configureLogStreams();
|
|
150
144
|
}
|
|
@@ -154,19 +148,19 @@ class EufySecurityPlatform {
|
|
|
154
148
|
configureLogStreams() {
|
|
155
149
|
// Log a message if log file storage will be omitted
|
|
156
150
|
if (this.config.omitLogFiles) {
|
|
157
|
-
|
|
151
|
+
log.info('log file storage will be omitted.');
|
|
158
152
|
return;
|
|
159
153
|
}
|
|
160
154
|
// Log streams configuration
|
|
161
155
|
const logStreams = [
|
|
162
|
-
{ name: 'eufy-security.log', logger:
|
|
163
|
-
{ name: 'ffmpeg.log', logger:
|
|
164
|
-
{ name: 'eufy-lib.log', logger:
|
|
156
|
+
{ name: 'eufy-security.log', logger: log },
|
|
157
|
+
{ name: 'ffmpeg.log', logger: ffmpegLogger },
|
|
158
|
+
{ name: 'eufy-lib.log', logger: tsLogger },
|
|
165
159
|
];
|
|
166
160
|
// Iterate over log streams
|
|
167
161
|
for (const { name, logger } of logStreams) {
|
|
168
162
|
// Create a log stream with specific configurations
|
|
169
|
-
const logStream =
|
|
163
|
+
const logStream = createStream(name, {
|
|
170
164
|
path: this.eufyPath, // Log file path
|
|
171
165
|
interval: '1d', // Log rotation interval (1 day)
|
|
172
166
|
rotate: 3, // Number of rotated log files to keep
|
|
@@ -197,18 +191,18 @@ class EufySecurityPlatform {
|
|
|
197
191
|
// Start off with a generic identifier.
|
|
198
192
|
this._hostSystem = 'generic';
|
|
199
193
|
// Take a look at the platform we're on for an initial hint of what we are.
|
|
200
|
-
switch (
|
|
194
|
+
switch (platform) {
|
|
201
195
|
// The beloved macOS.
|
|
202
196
|
case 'darwin':
|
|
203
197
|
// For macOS, we check the CPU model to determine if it's an Apple CPU or an Intel CPU.
|
|
204
|
-
this._hostSystem = 'macOS.' + (
|
|
198
|
+
this._hostSystem = 'macOS.' + (os.cpus()[0].model.includes('Apple') ? 'Apple' : 'Intel');
|
|
205
199
|
break;
|
|
206
200
|
// The indomitable Linux.
|
|
207
201
|
case 'linux':
|
|
208
202
|
// Let's further see if we're a small, but scrappy, Raspberry Pi.
|
|
209
203
|
try {
|
|
210
204
|
// As of the 4.9 kernel, Raspberry Pi prefers to be identified using this method and has deprecated cpuinfo.
|
|
211
|
-
const systemId =
|
|
205
|
+
const systemId = readFileSync('/sys/firmware/devicetree/base/model', { encoding: 'utf8' });
|
|
212
206
|
// Check if it's a Raspberry Pi 4.
|
|
213
207
|
if (/Raspberry Pi (Compute Module )?4/.test(systemId)) {
|
|
214
208
|
// If it's a Pi 4, we identify the system as running Raspbian.
|
|
@@ -231,12 +225,11 @@ class EufySecurityPlatform {
|
|
|
231
225
|
return this._hostSystem;
|
|
232
226
|
}
|
|
233
227
|
initSetup() {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
utils_1.log.debug('Using bropats @homebridge-eufy-security/eufy-security-client library in version ', eufy_security_client_1.libVersion);
|
|
228
|
+
log.debug('plugin data store:', this.eufyPath);
|
|
229
|
+
log.debug('OS is', this.hostSystem);
|
|
230
|
+
log.debug('Using bropats @homebridge-eufy-security/eufy-security-client library in version ', libVersion);
|
|
238
231
|
// Log the final configuration object for debugging purposes
|
|
239
|
-
|
|
232
|
+
log.debug('The config is:', this.config);
|
|
240
233
|
const eufyConfig = {
|
|
241
234
|
username: this.config.username,
|
|
242
235
|
password: this.config.password,
|
|
@@ -249,7 +242,7 @@ class EufySecurityPlatform {
|
|
|
249
242
|
enableEmbeddedPKCS1Support: this.config.enableEmbeddedPKCS1Support,
|
|
250
243
|
eventDurationSeconds: 10,
|
|
251
244
|
logging: {
|
|
252
|
-
level: (this.config.enableDetailedLogging) ?
|
|
245
|
+
level: (this.config.enableDetailedLogging) ? LogLevel.Debug : LogLevel.Info,
|
|
253
246
|
},
|
|
254
247
|
};
|
|
255
248
|
this.api.on("didFinishLaunching" /* APIEvent.DID_FINISH_LAUNCHING */, async () => {
|
|
@@ -258,33 +251,36 @@ class EufySecurityPlatform {
|
|
|
258
251
|
this.api.on("shutdown" /* APIEvent.SHUTDOWN */, async () => {
|
|
259
252
|
await this.pluginShutdown();
|
|
260
253
|
});
|
|
261
|
-
|
|
254
|
+
log.debug('Finished booting!');
|
|
262
255
|
}
|
|
263
256
|
async pluginSetup(eufyConfig) {
|
|
264
257
|
try {
|
|
265
|
-
this.eufyClient = await
|
|
258
|
+
this.eufyClient = await EufySecurity.initialize(eufyConfig, (this.config.enableDetailedLogging) ? tsLogger : undefined);
|
|
259
|
+
// Each camera adds listeners (livestream, talkback) on top of the base ones.
|
|
260
|
+
// Raise limit to prevent MaxListenersExceededWarning in Node 22+.
|
|
261
|
+
this.eufyClient.setMaxListeners(30);
|
|
266
262
|
this.eufyClient.on('station added', this.stationAdded.bind(this));
|
|
267
263
|
this.eufyClient.on('station removed', this.stationRemoved.bind(this));
|
|
268
264
|
this.eufyClient.on('device added', this.deviceAdded.bind(this));
|
|
269
265
|
this.eufyClient.on('device removed', this.deviceRemoved.bind(this));
|
|
270
266
|
this.eufyClient.on('push connect', () => {
|
|
271
|
-
|
|
267
|
+
log.debug('Push Connected!');
|
|
272
268
|
});
|
|
273
269
|
this.eufyClient.on('push close', () => {
|
|
274
|
-
|
|
270
|
+
log.debug('Push Closed!');
|
|
275
271
|
});
|
|
276
272
|
this.eufyClient.on('connect', () => {
|
|
277
|
-
|
|
273
|
+
log.debug('Connected!');
|
|
278
274
|
});
|
|
279
275
|
this.eufyClient.on('close', () => {
|
|
280
|
-
|
|
276
|
+
log.debug('Closed!');
|
|
281
277
|
});
|
|
282
278
|
this.eufyClient.on('connection error', async (error) => {
|
|
283
|
-
|
|
279
|
+
log.debug(`Error: ${error}`);
|
|
284
280
|
await this.pluginShutdown();
|
|
285
281
|
});
|
|
286
282
|
this.eufyClient.once('captcha request', async () => {
|
|
287
|
-
|
|
283
|
+
log.error(`
|
|
288
284
|
***************************
|
|
289
285
|
***** WARNING MESSAGE *****
|
|
290
286
|
***************************
|
|
@@ -293,13 +289,13 @@ class EufySecurityPlatform {
|
|
|
293
289
|
Please abstain from any activities until this period elapses...
|
|
294
290
|
Should your issue persist beyond this timeframe, you may need to consider setting up a new account.
|
|
295
291
|
For more detailed instructions, please consult:
|
|
296
|
-
https://github.com/homebridge-eufy-security/
|
|
292
|
+
https://github.com/homebridge-plugins/homebridge-eufy-security/wiki/Create-a-dedicated-admin-account-for-Homebridge-Eufy-Security-Plugin
|
|
297
293
|
***************************
|
|
298
294
|
`);
|
|
299
295
|
await this.pluginShutdown();
|
|
300
296
|
});
|
|
301
297
|
this.eufyClient.on('tfa request', async () => {
|
|
302
|
-
|
|
298
|
+
log.error(`
|
|
303
299
|
***************************
|
|
304
300
|
***** WARNING MESSAGE *****
|
|
305
301
|
***************************
|
|
@@ -308,28 +304,29 @@ class EufySecurityPlatform {
|
|
|
308
304
|
Kindly refrain from making any further attempts during this period...
|
|
309
305
|
If your concern remains unresolved after 24 hours, you may need to consider creating a new account.
|
|
310
306
|
For additional information, refer to:
|
|
311
|
-
https://github.com/homebridge-eufy-security/
|
|
307
|
+
https://github.com/homebridge-plugins/homebridge-eufy-security/wiki/Create-a-dedicated-admin-account-for-Homebridge-Eufy-Security-Plugin
|
|
312
308
|
***************************
|
|
313
309
|
`);
|
|
314
310
|
await this.pluginShutdown();
|
|
315
311
|
});
|
|
316
312
|
}
|
|
317
313
|
catch (e) {
|
|
318
|
-
|
|
319
|
-
|
|
314
|
+
log.error(`Error while setup : ${e}`);
|
|
315
|
+
log.error('Not connected can\'t continue!');
|
|
320
316
|
return;
|
|
321
317
|
}
|
|
322
318
|
try {
|
|
323
319
|
await this.eufyClient.connect();
|
|
324
|
-
|
|
320
|
+
log.debug('EufyClient connected ' + this.eufyClient.isConnected());
|
|
325
321
|
}
|
|
326
322
|
catch (e) {
|
|
327
|
-
|
|
323
|
+
log.error(`Error authenticating Eufy: ${e}`);
|
|
328
324
|
}
|
|
329
325
|
if (!this.eufyClient.isConnected()) {
|
|
330
|
-
|
|
326
|
+
log.error('Not connected can\'t continue!');
|
|
331
327
|
return;
|
|
332
328
|
}
|
|
329
|
+
log.info('Connected to Eufy. Discovering stations and devices... This may take up to 45 seconds.');
|
|
333
330
|
// give the connection 45 seconds to discover all devices
|
|
334
331
|
// then process pending stations and devices, and clean old accessories after that
|
|
335
332
|
this.cleanCachedAccessoriesTimeout = setTimeout(async () => {
|
|
@@ -338,10 +335,10 @@ class EufySecurityPlatform {
|
|
|
338
335
|
}, 45 * 1000);
|
|
339
336
|
if (this.config.CameraMaxLivestreamDuration > 86400) {
|
|
340
337
|
this.config.CameraMaxLivestreamDuration = 86400;
|
|
341
|
-
|
|
338
|
+
log.warn('Your maximum livestream duration value is too large. Since this can cause problems it was reset to 86400 seconds (1 day maximum).');
|
|
342
339
|
}
|
|
343
340
|
this.eufyClient.setCameraMaxLivestreamDuration(this.config.CameraMaxLivestreamDuration);
|
|
344
|
-
|
|
341
|
+
log.debug(`CameraMaxLivestreamDuration: ${this.eufyClient.getCameraMaxLivestreamDuration()}`);
|
|
345
342
|
}
|
|
346
343
|
/**
|
|
347
344
|
* Generates a UUID based on the given identifier and station flag.
|
|
@@ -353,28 +350,11 @@ class EufySecurityPlatform {
|
|
|
353
350
|
// Add prefix 's_' if it's a station identifier, otherwise, no prefix.
|
|
354
351
|
const prefix = isStation ? 's1_' : 'd1_';
|
|
355
352
|
// Generate UUID based on the prefix + identifier.
|
|
356
|
-
return
|
|
353
|
+
return HAP.uuid.generate(prefix + identifier);
|
|
357
354
|
}
|
|
358
355
|
async delay(ms) {
|
|
359
356
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
360
357
|
}
|
|
361
|
-
/**
|
|
362
|
-
* Checks if a device type is supported by the plugin.
|
|
363
|
-
* A device is considered supported if it matches at least one handler type:
|
|
364
|
-
* - Motion Sensor
|
|
365
|
-
* - Entry Sensor
|
|
366
|
-
* - Lock
|
|
367
|
-
* - Camera
|
|
368
|
-
*
|
|
369
|
-
* @param deviceType The device type to check
|
|
370
|
-
* @returns true if the device type is supported, false otherwise
|
|
371
|
-
*/
|
|
372
|
-
isSupportedDevice(deviceType) {
|
|
373
|
-
return (eufy_security_client_1.Device.isMotionSensor(deviceType) ||
|
|
374
|
-
eufy_security_client_1.Device.isEntrySensor(deviceType) ||
|
|
375
|
-
eufy_security_client_1.Device.isLock(deviceType) ||
|
|
376
|
-
eufy_security_client_1.Device.isCamera(deviceType));
|
|
377
|
-
}
|
|
378
358
|
/**
|
|
379
359
|
* Defines an accessory for a device or station.
|
|
380
360
|
*
|
|
@@ -392,7 +372,7 @@ class EufySecurityPlatform {
|
|
|
392
372
|
this.accessories.splice(this.accessories.indexOf(cachedAccessory), 1);
|
|
393
373
|
}
|
|
394
374
|
// Determine if the device is a camera
|
|
395
|
-
const isCamera = (deviceContainer.eufyDevice instanceof
|
|
375
|
+
const isCamera = (deviceContainer.eufyDevice instanceof Device)
|
|
396
376
|
? deviceContainer.eufyDevice.isCamera()
|
|
397
377
|
: false;
|
|
398
378
|
// Create a new accessory if not cached, otherwise use the cached one
|
|
@@ -426,7 +406,7 @@ class EufySecurityPlatform {
|
|
|
426
406
|
// Remove station or device accessories created prior to plugin upgrade,
|
|
427
407
|
// which may have been subject to removal due to newly introduced logic.
|
|
428
408
|
if (isExist) {
|
|
429
|
-
this.api.unregisterPlatformAccessories(
|
|
409
|
+
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
|
|
430
410
|
}
|
|
431
411
|
throw error;
|
|
432
412
|
}
|
|
@@ -437,100 +417,125 @@ class EufySecurityPlatform {
|
|
|
437
417
|
// Update or register the accessory with the platform
|
|
438
418
|
if (isExist) {
|
|
439
419
|
this.api.updatePlatformAccessories([accessory]);
|
|
440
|
-
|
|
420
|
+
log.info(`Updating existing accessory: ${accessory.displayName}`);
|
|
441
421
|
}
|
|
442
422
|
else {
|
|
443
|
-
this.api.registerPlatformAccessories(
|
|
444
|
-
|
|
423
|
+
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
|
|
424
|
+
log.info(`Registering new accessory: ${accessory.displayName}`);
|
|
445
425
|
}
|
|
446
426
|
}
|
|
447
427
|
catch (error) {
|
|
448
428
|
// Log any errors that occur during accessory addition or update
|
|
449
|
-
|
|
429
|
+
log.error(`Error in ${isStation ? 'stationAdded' : 'deviceAdded'}:`, error);
|
|
450
430
|
}
|
|
451
431
|
}
|
|
452
432
|
async stationAdded(station) {
|
|
453
433
|
try {
|
|
454
434
|
if (this.config.ignoreStations.includes(station.getSerial())) {
|
|
455
|
-
|
|
435
|
+
log.debug(`${station.getName()}: Station ignored`);
|
|
456
436
|
return;
|
|
457
437
|
}
|
|
458
438
|
const rawStation = station.getRawStation();
|
|
459
|
-
if (rawStation.member.member_type !==
|
|
439
|
+
if (rawStation.member.member_type !== UserType.ADMIN) {
|
|
460
440
|
await this.pluginShutdown();
|
|
461
|
-
|
|
441
|
+
log.error(`
|
|
462
442
|
#########################
|
|
463
443
|
######### ERROR #########
|
|
464
444
|
#########################
|
|
465
445
|
You're not using a guest admin account with this plugin! You must use a guest admin account!
|
|
466
446
|
Please look here for more details:
|
|
467
|
-
https://github.com/homebridge-eufy-security/
|
|
447
|
+
https://github.com/homebridge-plugins/homebridge-eufy-security/wiki/Create-a-dedicated-admin-account-for-Homebridge-Eufy-Security-Plugin
|
|
468
448
|
#########################
|
|
469
449
|
`);
|
|
470
450
|
return;
|
|
471
451
|
}
|
|
472
452
|
// Store station for batch processing later
|
|
473
453
|
this.pendingStations.push(station);
|
|
474
|
-
|
|
454
|
+
log.debug(`${station.getName()}: Station queued for processing`);
|
|
475
455
|
}
|
|
476
456
|
catch (error) {
|
|
477
|
-
|
|
457
|
+
log.error(`Error in stationAdded:, ${error}`);
|
|
478
458
|
}
|
|
479
459
|
}
|
|
480
460
|
async deviceAdded(device) {
|
|
481
461
|
try {
|
|
482
462
|
if (this.config.ignoreDevices.includes(device.getSerial())) {
|
|
483
|
-
|
|
463
|
+
log.debug(`${device.getName()}: Device ignored`);
|
|
484
464
|
return;
|
|
485
465
|
}
|
|
486
466
|
// Check if the device is a keypad and ignore it from the start
|
|
487
467
|
const deviceType = device.getDeviceType();
|
|
488
|
-
if (
|
|
489
|
-
|
|
468
|
+
if (Device.isKeyPad(deviceType)) {
|
|
469
|
+
log.warn(`${device.getName()}: The keypad is ignored as it serves no purpose in this plugin. You can ignore this message.`);
|
|
490
470
|
return;
|
|
491
471
|
}
|
|
492
472
|
// Store device for batch processing later
|
|
493
473
|
this.pendingDevices.push(device);
|
|
494
|
-
|
|
474
|
+
log.debug(`${device.getName()}: Device queued for processing`);
|
|
495
475
|
}
|
|
496
476
|
catch (error) {
|
|
497
|
-
|
|
477
|
+
log.error(`Error in deviceAdded: ${error}`);
|
|
498
478
|
}
|
|
499
479
|
}
|
|
500
480
|
async stationRemoved(station) {
|
|
501
481
|
const serial = station.getSerial();
|
|
502
|
-
|
|
482
|
+
log.debug(`A device has been removed: ${serial}`);
|
|
503
483
|
}
|
|
504
484
|
async deviceRemoved(device) {
|
|
505
485
|
const serial = device.getSerial();
|
|
506
|
-
|
|
486
|
+
log.debug(`A device has been removed: ${serial}`);
|
|
507
487
|
}
|
|
508
488
|
/**
|
|
509
489
|
* Processes pending stations and devices in batch.
|
|
510
490
|
* Only creates station entities if:
|
|
511
491
|
* 1. It's a real HomeBase (DeviceType.STATION) OR
|
|
512
|
-
* 2. It has at least one
|
|
492
|
+
* 2. It has at least one device attached
|
|
513
493
|
*
|
|
514
|
-
*
|
|
494
|
+
* All discovered devices are processed since they are already verified by bropat/eufy-security-client.
|
|
515
495
|
*/
|
|
516
496
|
async processPendingDevices() {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
497
|
+
log.debug(`[PROCESSING START] Processing ${this.pendingStations.length} stations and ${this.pendingDevices.length} devices`);
|
|
498
|
+
if (this.pendingStations.length === 0 || this.pendingDevices.length === 0) {
|
|
499
|
+
log.warn(`[DISCOVERY WARNING] Discovery finished with ${this.pendingStations.length} station(s) and ${this.pendingDevices.length} devices(s). ` +
|
|
500
|
+
'If this is unexpected, please verify your Eufy account has devices and the credentials used are for a guest admin account.');
|
|
501
|
+
}
|
|
502
|
+
// Build set of stations that have at least one device
|
|
503
|
+
const stationsWithDevices = new Set();
|
|
504
|
+
// Create all queued devices (they are already verified by bropat/eufy-security-client)
|
|
505
|
+
log.debug(`[DEVICES PROCESSING] Starting to process ${this.pendingDevices.length} devices`);
|
|
520
506
|
for (const device of this.pendingDevices) {
|
|
521
|
-
|
|
522
|
-
|
|
507
|
+
stationsWithDevices.add(device.getStationSerial());
|
|
508
|
+
try {
|
|
509
|
+
const deviceType = device.getDeviceType();
|
|
510
|
+
const deviceContainer = {
|
|
511
|
+
deviceIdentifier: {
|
|
512
|
+
uniqueId: device.getSerial(),
|
|
513
|
+
displayName: 'DEVICE ' + device.getName().replace(/[^a-zA-Z0-9]/g, ''),
|
|
514
|
+
type: deviceType,
|
|
515
|
+
},
|
|
516
|
+
eufyDevice: device,
|
|
517
|
+
};
|
|
518
|
+
log.debug(`[DEVICE] Processing: ${deviceContainer.deviceIdentifier.displayName}`);
|
|
519
|
+
await this.addOrUpdateAccessory(deviceContainer, false);
|
|
520
|
+
log.debug(`[DEVICE] Completed: ${deviceContainer.deviceIdentifier.displayName}`);
|
|
521
|
+
}
|
|
522
|
+
catch (error) {
|
|
523
|
+
log.error(`[DEVICE ERROR] Error processing device "${device.getName()}": ${error}`);
|
|
523
524
|
}
|
|
524
525
|
}
|
|
526
|
+
log.debug(`[DEVICES COMPLETE] All ${this.pendingDevices.length} devices processed`);
|
|
525
527
|
// Create queued stations that should be created
|
|
528
|
+
log.debug(`[STATIONS PROCESSING] Starting to process ${this.pendingStations.length} stations`);
|
|
529
|
+
let stationsSkipped = 0;
|
|
526
530
|
for (const station of this.pendingStations) {
|
|
527
531
|
const stationType = station.getDeviceType();
|
|
528
532
|
const stationSerial = station.getSerial();
|
|
529
|
-
// Create if: it's a real HomeBase OR it has
|
|
530
|
-
const shouldCreate = stationType ===
|
|
531
|
-
|
|
533
|
+
// Create if: it's a real HomeBase OR it has devices
|
|
534
|
+
const shouldCreate = stationType === DeviceType.STATION ||
|
|
535
|
+
stationsWithDevices.has(stationSerial);
|
|
532
536
|
if (!shouldCreate) {
|
|
533
|
-
|
|
537
|
+
stationsSkipped++;
|
|
538
|
+
log.debug(`[STATION SKIP] "${station.getName()}" has no devices and will be skipped`);
|
|
534
539
|
continue;
|
|
535
540
|
}
|
|
536
541
|
try {
|
|
@@ -542,43 +547,19 @@ class EufySecurityPlatform {
|
|
|
542
547
|
},
|
|
543
548
|
eufyDevice: station,
|
|
544
549
|
};
|
|
545
|
-
|
|
546
|
-
utils_1.log.debug(`${deviceContainer.deviceIdentifier.displayName} pre-caching complete`);
|
|
550
|
+
log.debug(`[STATION] Processing: ${deviceContainer.deviceIdentifier.displayName}`);
|
|
547
551
|
await this.addOrUpdateAccessory(deviceContainer, true);
|
|
552
|
+
log.debug(`[STATION] Completed: ${deviceContainer.deviceIdentifier.displayName}`);
|
|
548
553
|
}
|
|
549
554
|
catch (error) {
|
|
550
|
-
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
// Create queued devices that are supported
|
|
554
|
-
for (const device of this.pendingDevices) {
|
|
555
|
-
const deviceType = device.getDeviceType();
|
|
556
|
-
// Skip unsupported devices
|
|
557
|
-
if (!this.isSupportedDevice(deviceType)) {
|
|
558
|
-
utils_1.log.debug(`${device.getName()}: Device type ${deviceType} not supported, skipping`);
|
|
559
|
-
continue;
|
|
560
|
-
}
|
|
561
|
-
try {
|
|
562
|
-
const deviceContainer = {
|
|
563
|
-
deviceIdentifier: {
|
|
564
|
-
uniqueId: device.getSerial(),
|
|
565
|
-
displayName: 'DEVICE ' + device.getName().replace(/[^a-zA-Z0-9]/g, ''),
|
|
566
|
-
type: deviceType,
|
|
567
|
-
},
|
|
568
|
-
eufyDevice: device,
|
|
569
|
-
};
|
|
570
|
-
await this.delay(settings_1.DEVICE_INIT_DELAY);
|
|
571
|
-
utils_1.log.debug(`${deviceContainer.deviceIdentifier.displayName} pre-caching complete`);
|
|
572
|
-
await this.addOrUpdateAccessory(deviceContainer, false);
|
|
573
|
-
}
|
|
574
|
-
catch (error) {
|
|
575
|
-
utils_1.log.error(`Error processing device "${device.getName()}": ${error}`);
|
|
555
|
+
log.error(`[STATION ERROR] Error processing station "${station.getName()}": ${error}`);
|
|
576
556
|
}
|
|
577
557
|
}
|
|
558
|
+
log.debug(`[STATIONS COMPLETE] ${this.pendingStations.length - stationsSkipped} stations created, ${stationsSkipped} skipped`);
|
|
578
559
|
// Clear pending queues
|
|
579
560
|
this.pendingStations = [];
|
|
580
561
|
this.pendingDevices = [];
|
|
581
|
-
|
|
562
|
+
log.info(`[PROCESSING COMPLETE] All pending devices and stations processed`);
|
|
582
563
|
}
|
|
583
564
|
async pluginShutdown() {
|
|
584
565
|
// Ensure a single shutdown to prevent corruption of the persistent file.
|
|
@@ -594,10 +575,10 @@ class EufySecurityPlatform {
|
|
|
594
575
|
if (this.eufyClient.isConnected()) {
|
|
595
576
|
this.eufyClient.close();
|
|
596
577
|
}
|
|
597
|
-
|
|
578
|
+
log.info('Finished shutdown!');
|
|
598
579
|
}
|
|
599
580
|
catch (e) {
|
|
600
|
-
|
|
581
|
+
log.error(`Error while shutdown: ${e}`);
|
|
601
582
|
}
|
|
602
583
|
}
|
|
603
584
|
/**
|
|
@@ -605,68 +586,67 @@ class EufySecurityPlatform {
|
|
|
605
586
|
* It should be used to setup event handlers for characteristics and update respective values.
|
|
606
587
|
*/
|
|
607
588
|
configureAccessory(accessory) {
|
|
608
|
-
|
|
589
|
+
log.debug(`Loading accessory from cache: ${accessory.displayName}`);
|
|
609
590
|
// add the restored accessory to the accessories cache so we can track if it has already been registered
|
|
610
591
|
this.accessories.push(accessory);
|
|
611
592
|
}
|
|
612
593
|
cleanCachedAccessories() {
|
|
613
594
|
if (this.config.cleanCache) {
|
|
614
|
-
|
|
595
|
+
log.debug('Looking for old cached accessories that seem to be outdated...');
|
|
615
596
|
let num = 0;
|
|
616
597
|
const staleAccessories = this.accessories.filter((item) => {
|
|
617
598
|
return this.activeAccessoryIds.indexOf(item.UUID) === -1;
|
|
618
599
|
});
|
|
619
600
|
staleAccessories.forEach((staleAccessory) => {
|
|
620
|
-
|
|
601
|
+
log.info(`Removing cached accessory ${staleAccessory.UUID} ${staleAccessory.displayName}`);
|
|
621
602
|
num++;
|
|
622
|
-
this.api.unregisterPlatformAccessories(
|
|
603
|
+
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [staleAccessory]);
|
|
623
604
|
});
|
|
624
605
|
if (num > 0) {
|
|
625
|
-
|
|
606
|
+
log.info('Removed ' + num + ' cached accessories');
|
|
626
607
|
}
|
|
627
608
|
else {
|
|
628
|
-
|
|
609
|
+
log.info('No outdated cached accessories found.');
|
|
629
610
|
}
|
|
630
611
|
}
|
|
631
612
|
}
|
|
632
613
|
register_station(accessory, container) {
|
|
633
|
-
|
|
614
|
+
log.debug(accessory.displayName + ' UUID:' + accessory.UUID);
|
|
634
615
|
const type = container.deviceIdentifier.type;
|
|
635
616
|
const station = container.eufyDevice;
|
|
636
|
-
if (type !==
|
|
617
|
+
if (type !== DeviceType.STATION) {
|
|
637
618
|
// Standalone Lock or Doorbell doesn't have Security Control
|
|
638
|
-
if (
|
|
619
|
+
if (Device.isDoorbell(type) || Device.isLock(type)) {
|
|
639
620
|
throw new Error(`looks station but it's not could imply some errors! Type: ${type}. You can ignore this message.`);
|
|
640
621
|
}
|
|
641
622
|
}
|
|
642
623
|
if (this.config.autoSyncStation) {
|
|
643
|
-
new
|
|
624
|
+
new AutoSyncStationAccessory(this, accessory, station);
|
|
644
625
|
}
|
|
645
626
|
else {
|
|
646
|
-
new
|
|
627
|
+
new StationAccessory(this, accessory, station);
|
|
647
628
|
}
|
|
648
629
|
}
|
|
649
630
|
register_device(accessory, container) {
|
|
650
|
-
|
|
631
|
+
log.debug(accessory.displayName + ' UUID:' + accessory.UUID);
|
|
651
632
|
const device = container.eufyDevice;
|
|
652
633
|
const type = container.deviceIdentifier.type;
|
|
653
|
-
if (
|
|
654
|
-
|
|
655
|
-
new
|
|
634
|
+
if (Device.isMotionSensor(type)) {
|
|
635
|
+
log.debug(accessory.displayName + ' isMotionSensor!');
|
|
636
|
+
new MotionSensorAccessory(this, accessory, device);
|
|
656
637
|
}
|
|
657
|
-
if (
|
|
658
|
-
|
|
659
|
-
new
|
|
638
|
+
if (Device.isEntrySensor(type)) {
|
|
639
|
+
log.debug(accessory.displayName + ' isEntrySensor!');
|
|
640
|
+
new EntrySensorAccessory(this, accessory, device);
|
|
660
641
|
}
|
|
661
|
-
if (
|
|
662
|
-
|
|
663
|
-
new
|
|
642
|
+
if (Device.isLock(type)) {
|
|
643
|
+
log.debug(accessory.displayName + ' isLock!');
|
|
644
|
+
new LockAccessory(this, accessory, device);
|
|
664
645
|
}
|
|
665
|
-
if (
|
|
666
|
-
|
|
667
|
-
new
|
|
646
|
+
if (Device.isCamera(type)) {
|
|
647
|
+
log.debug(accessory.displayName + ' isCamera!');
|
|
648
|
+
new CameraAccessory(this, accessory, device);
|
|
668
649
|
}
|
|
669
650
|
}
|
|
670
651
|
}
|
|
671
|
-
exports.EufySecurityPlatform = EufySecurityPlatform;
|
|
672
652
|
//# sourceMappingURL=platform.js.map
|