@homebridge-plugins/homebridge-eufy-security 0.0.1
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 +5 -0
- package/FUNDING.yml +1 -0
- package/LICENSE +176 -0
- package/README.md +67 -0
- package/config.schema.json +6 -0
- package/dist/accessories/AutoSyncStationAccessory.js +156 -0
- package/dist/accessories/AutoSyncStationAccessory.js.map +1 -0
- package/dist/accessories/BaseAccessory.js +247 -0
- package/dist/accessories/BaseAccessory.js.map +1 -0
- package/dist/accessories/CameraAccessory.js +431 -0
- package/dist/accessories/CameraAccessory.js.map +1 -0
- package/dist/accessories/Device.js +67 -0
- package/dist/accessories/Device.js.map +1 -0
- package/dist/accessories/EntrySensorAccessory.js +48 -0
- package/dist/accessories/EntrySensorAccessory.js.map +1 -0
- package/dist/accessories/LockAccessory.js +142 -0
- package/dist/accessories/LockAccessory.js.map +1 -0
- package/dist/accessories/MotionSensorAccessory.js +48 -0
- package/dist/accessories/MotionSensorAccessory.js.map +1 -0
- package/dist/accessories/SmartDropAccessory.js +145 -0
- package/dist/accessories/SmartDropAccessory.js.map +1 -0
- package/dist/accessories/StationAccessory.js +371 -0
- package/dist/accessories/StationAccessory.js.map +1 -0
- package/dist/config.js +25 -0
- package/dist/config.js.map +1 -0
- package/dist/controller/LocalLivestreamManager.js +116 -0
- package/dist/controller/LocalLivestreamManager.js.map +1 -0
- package/dist/controller/recordingDelegate.js +208 -0
- package/dist/controller/recordingDelegate.js.map +1 -0
- package/dist/controller/snapshotDelegate.js +345 -0
- package/dist/controller/snapshotDelegate.js.map +1 -0
- package/dist/controller/streamingDelegate.js +345 -0
- package/dist/controller/streamingDelegate.js.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces.js +2 -0
- package/dist/interfaces.js.map +1 -0
- package/dist/media/Snapshot-Unavailable.png +0 -0
- package/dist/media/Snapshot-Unavailable.xcf +0 -0
- package/dist/media/Snapshot-black.png +0 -0
- package/dist/media/camera-disabled.png +0 -0
- package/dist/media/camera-offline.png +0 -0
- package/dist/media/media/Snapshot-Unavailable.png +0 -0
- package/dist/media/media/Snapshot-Unavailable.xcf +0 -0
- package/dist/media/media/Snapshot-black.png +0 -0
- package/dist/media/media/camera-disabled.png +0 -0
- package/dist/media/media/camera-offline.png +0 -0
- package/dist/platform.js +716 -0
- package/dist/platform.js.map +1 -0
- package/dist/settings.js +38 -0
- package/dist/settings.js.map +1 -0
- package/dist/utils/Talkback.js +92 -0
- package/dist/utils/Talkback.js.map +1 -0
- package/dist/utils/accessoriesStore.js +206 -0
- package/dist/utils/accessoriesStore.js.map +1 -0
- package/dist/utils/configTypes.js +35 -0
- package/dist/utils/configTypes.js.map +1 -0
- package/dist/utils/ffmpeg.js +843 -0
- package/dist/utils/ffmpeg.js.map +1 -0
- package/dist/utils/interfaces.js +8 -0
- package/dist/utils/interfaces.js.map +1 -0
- package/dist/utils/utils.js +44 -0
- package/dist/utils/utils.js.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/eslint.config.mjs +18 -0
- package/homebridge-eufy-security.png +0 -0
- package/homebridge-ui/public/app.js +225 -0
- package/homebridge-ui/public/assets/devices/4g_lte_starlight_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/BATTERY_DOORBELL_C30.png +0 -0
- package/homebridge-ui/public/assets/devices/BATTERY_DOORBELL_C31.png +0 -0
- package/homebridge-ui/public/assets/devices/batterydoorbell1080p_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/batterydoorbell2kdual_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/batterydoorbell_e340_large.png +0 -0
- package/homebridge-ui/public/assets/devices/eufy-security-client.png +0 -0
- package/homebridge-ui/public/assets/devices/eufycam2_large.png +0 -0
- package/homebridge-ui/public/assets/devices/eufycam2c_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/eufycam2cpro_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/eufycam2pro_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/eufycam3_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/eufycam3c_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/eufycam3pro_large.png +0 -0
- package/homebridge-ui/public/assets/devices/eufycam_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/eufycame330_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/floodlight2_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/floodlight2pro_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/floodlight_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/floodlightcame340_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/garage_camera_t8452_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/homebase2_large.png +0 -0
- package/homebridge-ui/public/assets/devices/homebase3_large.png +0 -0
- package/homebridge-ui/public/assets/devices/homebase_large.jpg +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/indoorcamc120_large.png +0 -0
- package/homebridge-ui/public/assets/devices/indoorcammini_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/indoorcamp24_large.png +0 -0
- package/homebridge-ui/public/assets/devices/indoorcams350_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/keypad_large.png +0 -0
- package/homebridge-ui/public/assets/devices/minibase_chime_T8023_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/motionsensor_large.png +0 -0
- package/homebridge-ui/public/assets/devices/sensor_large.png +0 -0
- package/homebridge-ui/public/assets/devices/smartdrop_t8790_large.png +0 -0
- package/homebridge-ui/public/assets/devices/smartlock_t8500_large.png +0 -0
- package/homebridge-ui/public/assets/devices/smartlock_t8500_wifibridge_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/smartlock_t8503_large.png +0 -0
- package/homebridge-ui/public/assets/devices/smartlock_t8504_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/smartlock_t8510P_t8520P_large.png +0 -0
- package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8502_large.png +0 -0
- package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8506_large.png +0 -0
- package/homebridge-ui/public/assets/devices/smartlock_touch_and_wifi_t8520_large.png +0 -0
- package/homebridge-ui/public/assets/devices/smartlock_touch_t8510_large.png +0 -0
- package/homebridge-ui/public/assets/devices/smartlock_touch_t8510_wifibridge_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/smartlock_video_t8530_large.png +0 -0
- package/homebridge-ui/public/assets/devices/smartlockwifibridge_t8021_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/smartsafe_s10_t7400_large.png +0 -0
- package/homebridge-ui/public/assets/devices/smartsafe_s12_t7401_large.png +0 -0
- package/homebridge-ui/public/assets/devices/smarttrack_card_t87B2_large.png +0 -0
- package/homebridge-ui/public/assets/devices/smarttrack_link_t87B0_large.png +0 -0
- package/homebridge-ui/public/assets/devices/solocamc210_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/solocamc35_large.png +0 -0
- package/homebridge-ui/public/assets/devices/solocame20_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/solocame30_large.png +0 -0
- package/homebridge-ui/public/assets/devices/solocame40_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/solocaml20_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/solocams220_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/solocams340_large.png +0 -0
- package/homebridge-ui/public/assets/devices/solocams40_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/soloindoorcamc24_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/solooutdoorcamc22_large.png +0 -0
- package/homebridge-ui/public/assets/devices/solooutdoorcamc24_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/unknown.png +0 -0
- package/homebridge-ui/public/assets/devices/walllight_s100_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/walllight_s120_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/wireddoorbell1080p_large.jpg +0 -0
- package/homebridge-ui/public/assets/devices/wireddoorbell2k_large.png +0 -0
- package/homebridge-ui/public/assets/devices/wireddoorbelldual_large.jpg +0 -0
- package/homebridge-ui/public/assets/icons/attach.svg +1 -0
- package/homebridge-ui/public/assets/icons/battery_0.svg +1 -0
- package/homebridge-ui/public/assets/icons/battery_1.svg +1 -0
- package/homebridge-ui/public/assets/icons/battery_2.svg +1 -0
- package/homebridge-ui/public/assets/icons/battery_3.svg +1 -0
- package/homebridge-ui/public/assets/icons/battery_4.svg +1 -0
- package/homebridge-ui/public/assets/icons/battery_5.svg +1 -0
- package/homebridge-ui/public/assets/icons/battery_6.svg +1 -0
- package/homebridge-ui/public/assets/icons/bolt.svg +1 -0
- package/homebridge-ui/public/assets/icons/bug-report.svg +1 -0
- package/homebridge-ui/public/assets/icons/copy.svg +1 -0
- package/homebridge-ui/public/assets/icons/delete.svg +1 -0
- package/homebridge-ui/public/assets/icons/download.svg +1 -0
- package/homebridge-ui/public/assets/icons/info.svg +1 -0
- package/homebridge-ui/public/assets/icons/inventory.svg +1 -0
- package/homebridge-ui/public/assets/icons/refresh.svg +1 -0
- package/homebridge-ui/public/assets/icons/satellite_alt.svg +1 -0
- package/homebridge-ui/public/assets/icons/settings.svg +1 -0
- package/homebridge-ui/public/assets/icons/settings_backup_restore.svg +1 -0
- package/homebridge-ui/public/assets/icons/solar_power.svg +1 -0
- package/homebridge-ui/public/assets/icons/warning.svg +1 -0
- package/homebridge-ui/public/components/device-card.js +162 -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 +27 -0
- package/homebridge-ui/public/services/api.js +214 -0
- package/homebridge-ui/public/services/config.js +144 -0
- package/homebridge-ui/public/style.css +775 -0
- package/homebridge-ui/public/utils/countries.js +73 -0
- package/homebridge-ui/public/utils/device-images.js +89 -0
- package/homebridge-ui/public/utils/helpers.js +87 -0
- package/homebridge-ui/public/views/dashboard.js +226 -0
- package/homebridge-ui/public/views/device-detail.js +610 -0
- package/homebridge-ui/public/views/diagnostics.js +296 -0
- package/homebridge-ui/public/views/login.js +636 -0
- package/homebridge-ui/public/views/settings.js +192 -0
- package/homebridge-ui/public/views/unsupported-detail.js +296 -0
- package/homebridge-ui/server.js +1327 -0
- package/media/Snapshot-Unavailable.png +0 -0
- package/media/Snapshot-Unavailable.xcf +0 -0
- package/media/Snapshot-black.png +0 -0
- package/media/camera-disabled.png +0 -0
- package/media/camera-offline.png +0 -0
- package/package.json +64 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API service — wraps homebridge.request() and push event listeners.
|
|
3
|
+
* Communicates with server.ts endpoints.
|
|
4
|
+
*/
|
|
5
|
+
// eslint-disable-next-line no-unused-vars
|
|
6
|
+
const Api = {
|
|
7
|
+
/** @private Track registered listeners to prevent stacking on re-render */
|
|
8
|
+
_listeners: {},
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Register an event listener, replacing any previous listener for the same event.
|
|
12
|
+
* Prevents listener stacking when views re-render.
|
|
13
|
+
* @param {string} event
|
|
14
|
+
* @param {function} handler
|
|
15
|
+
*/
|
|
16
|
+
_on(event, handler) {
|
|
17
|
+
if (this._listeners[event]) {
|
|
18
|
+
homebridge.removeEventListener(event, this._listeners[event]);
|
|
19
|
+
}
|
|
20
|
+
this._listeners[event] = handler;
|
|
21
|
+
homebridge.addEventListener(event, handler);
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Remove a previously registered listener for an event.
|
|
26
|
+
* Views can call this on teardown to explicitly detach their callbacks.
|
|
27
|
+
* @param {string} event
|
|
28
|
+
*/
|
|
29
|
+
_off(event) {
|
|
30
|
+
if (this._listeners[event]) {
|
|
31
|
+
homebridge.removeEventListener(event, this._listeners[event]);
|
|
32
|
+
delete this._listeners[event];
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Login with credentials, TFA code, or captcha
|
|
38
|
+
* @param {object} options - { username, password, country, deviceName } | { verifyCode } | { captcha: { captchaCode, captchaId } }
|
|
39
|
+
* @returns {Promise<{success: boolean, failReason?: number, data?: any}>}
|
|
40
|
+
*/
|
|
41
|
+
async login(options) {
|
|
42
|
+
return homebridge.request('/login', options);
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Check if a valid persistent cache file exists on the server.
|
|
47
|
+
* @returns {Promise<{valid: boolean}>}
|
|
48
|
+
*/
|
|
49
|
+
async checkCache() {
|
|
50
|
+
return homebridge.request('/checkCache');
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Load stored accessories from server (cached from last login)
|
|
55
|
+
* @returns {Promise<Array>} Array of L_Station objects
|
|
56
|
+
*/
|
|
57
|
+
async loadStoredAccessories() {
|
|
58
|
+
return homebridge.request('/storedAccessories');
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Load unsupported device intel from server (raw properties for triage)
|
|
63
|
+
* @returns {Promise<{devices: Array}>} Array of unsupported device entries
|
|
64
|
+
*/
|
|
65
|
+
async loadUnsupportedDevices() {
|
|
66
|
+
return homebridge.request('/unsupportedDevices');
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Reset plugin data (removes persistent storage)
|
|
71
|
+
* @returns {Promise<{result: number}>}
|
|
72
|
+
*/
|
|
73
|
+
async resetPlugin() {
|
|
74
|
+
return homebridge.request('/reset');
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Download compressed diagnostics archive (logs + accessories)
|
|
79
|
+
* @returns {Promise<{buffer: Buffer, filename: string}>}
|
|
80
|
+
*/
|
|
81
|
+
async downloadDiagnostics() {
|
|
82
|
+
return homebridge.request('/downloadDiagnostics');
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Delete all files except persistent data and accessories
|
|
87
|
+
* @returns {Promise<{deleted: number}>}
|
|
88
|
+
*/
|
|
89
|
+
async cleanStorage() {
|
|
90
|
+
return homebridge.request('/cleanStorage');
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Register a listener for the 'addAccessory' push event.
|
|
95
|
+
* Fired by server after batch processing completes (~45s after login).
|
|
96
|
+
* Replaces any previously registered listener.
|
|
97
|
+
* @param {function} callback - receives array of L_Station objects
|
|
98
|
+
*/
|
|
99
|
+
onAccessoriesReady(callback) {
|
|
100
|
+
this._on('addAccessory', (event) => {
|
|
101
|
+
callback(event.data);
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Register a listener for discovery warnings (e.g. extended discovery due to unsupported devices)
|
|
107
|
+
* @param {function} callback - receives { unsupportedCount, unsupportedNames, waitSeconds, message }
|
|
108
|
+
*/
|
|
109
|
+
onDiscoveryWarning(callback) {
|
|
110
|
+
this._on('discoveryWarning', (event) => {
|
|
111
|
+
callback(event.data);
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Register a listener for real-time discovery progress events.
|
|
117
|
+
* @param {function} callback - receives { phase, stations?, devices?, message }
|
|
118
|
+
*/
|
|
119
|
+
onDiscoveryProgress(callback) {
|
|
120
|
+
this._on('discoveryProgress', (event) => {
|
|
121
|
+
callback(event.data);
|
|
122
|
+
});
|
|
123
|
+
},
|
|
124
|
+
/**
|
|
125
|
+
* Register a listener for TFA (two-factor auth) request from the server.
|
|
126
|
+
* Fired when the Eufy server requires a verification code.
|
|
127
|
+
* @param {function} callback - receives no arguments
|
|
128
|
+
*/
|
|
129
|
+
onTfaRequest(callback) {
|
|
130
|
+
this._on('tfaRequest', () => callback());
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Register a listener for Captcha request from the server.
|
|
135
|
+
* Fired when the Eufy server requires captcha verification.
|
|
136
|
+
* @param {function} callback - receives { id, captcha }
|
|
137
|
+
*/
|
|
138
|
+
onCaptchaRequest(callback) {
|
|
139
|
+
this._on('captchaRequest', (event) => callback(event.data));
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Register a listener for successful authentication.
|
|
144
|
+
* @param {function} callback - receives no arguments
|
|
145
|
+
*/
|
|
146
|
+
onAuthSuccess(callback) {
|
|
147
|
+
this._on('authSuccess', () => callback());
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Register a listener for authentication errors (timeout, bad credentials, etc.).
|
|
152
|
+
* @param {function} callback - receives { message }
|
|
153
|
+
*/
|
|
154
|
+
onAuthError(callback) {
|
|
155
|
+
this._on('authError', (event) => callback(event.data));
|
|
156
|
+
},
|
|
157
|
+
/**
|
|
158
|
+
* Tell the server to skip the unsupported-device intel wait and proceed immediately.
|
|
159
|
+
* @returns {Promise<{ok: boolean}>}
|
|
160
|
+
*/
|
|
161
|
+
async skipIntelWait() {
|
|
162
|
+
return homebridge.request('/skipIntelWait');
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get the current discovery state (phase + pending counts).
|
|
167
|
+
* Used by the discovery UI to catch up on events that fired during login.
|
|
168
|
+
* @returns {Promise<{phase: string, stations: number, devices: number}>}
|
|
169
|
+
*/
|
|
170
|
+
async getDiscoveryState() {
|
|
171
|
+
return homebridge.request('/discoveryState');
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Register a listener for admin account error.
|
|
176
|
+
* Replaces any previously registered listener.
|
|
177
|
+
* @param {function} callback
|
|
178
|
+
*/
|
|
179
|
+
onAdminAccountUsed(callback) {
|
|
180
|
+
this._on('AdminAccountUsed', () => {
|
|
181
|
+
callback();
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Register a listener for cache warnings (stale, version mismatch).
|
|
187
|
+
* Replaces any previously registered listener.
|
|
188
|
+
* @param {function} callback - receives { reason, ageDays?, currentVersion?, storedVersion? }
|
|
189
|
+
*/
|
|
190
|
+
onCacheWarning(callback) {
|
|
191
|
+
this._on('cacheWarning', (event) => {
|
|
192
|
+
callback(event.data);
|
|
193
|
+
});
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Register a listener for diagnostics download progress.
|
|
198
|
+
* Replaces any previously registered listener.
|
|
199
|
+
* @param {function} callback - receives { progress, status }
|
|
200
|
+
*/
|
|
201
|
+
onDiagnosticsProgress(callback) {
|
|
202
|
+
this._on('diagnosticsProgress', (event) => {
|
|
203
|
+
callback(event.data);
|
|
204
|
+
});
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Get system and environment information for issue reporting
|
|
209
|
+
* @returns {Promise<{pluginVersion: string, eufyClientVersion: string, homebridgeVersion: string, nodeVersion: string, os: string, devices: Array}>}
|
|
210
|
+
*/
|
|
211
|
+
async getSystemInfo() {
|
|
212
|
+
return homebridge.request('/systemInfo');
|
|
213
|
+
},
|
|
214
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config service — wraps homebridge plugin config CRUD operations.
|
|
3
|
+
* Manages reading/writing plugin configuration via the Homebridge API.
|
|
4
|
+
*/
|
|
5
|
+
// eslint-disable-next-line no-unused-vars
|
|
6
|
+
const Config = {
|
|
7
|
+
_cache: null,
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get the current plugin config (first block).
|
|
11
|
+
* Uses cache if available; call load() to force a fresh read.
|
|
12
|
+
* @returns {Promise<object>}
|
|
13
|
+
*/
|
|
14
|
+
async get() {
|
|
15
|
+
if (this._cache) return this._cache;
|
|
16
|
+
return this.load();
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Load config from Homebridge (always fetches fresh, no side-effects).
|
|
21
|
+
* @returns {Promise<object>}
|
|
22
|
+
*/
|
|
23
|
+
async load() {
|
|
24
|
+
const configs = await homebridge.getPluginConfig();
|
|
25
|
+
this._cache = configs.length > 0 ? configs[0] : {};
|
|
26
|
+
return this._cache;
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Update config in memory (does NOT save to disk).
|
|
31
|
+
* @param {object} config - full config object
|
|
32
|
+
*/
|
|
33
|
+
async update(config) {
|
|
34
|
+
this._cache = config;
|
|
35
|
+
await homebridge.updatePluginConfig([config]);
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Save current config to disk (config.json).
|
|
40
|
+
* Call sparingly — only after login or explicit user save.
|
|
41
|
+
*/
|
|
42
|
+
async save() {
|
|
43
|
+
await homebridge.savePluginConfig();
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Update config and immediately save to disk.
|
|
48
|
+
* @param {object} config
|
|
49
|
+
*/
|
|
50
|
+
async updateAndSave(config) {
|
|
51
|
+
await this.update(config);
|
|
52
|
+
await this.save();
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get device config for a specific device by serial number.
|
|
57
|
+
* @param {string} serialNumber
|
|
58
|
+
* @returns {object|undefined}
|
|
59
|
+
*/
|
|
60
|
+
getDeviceConfig(serialNumber) {
|
|
61
|
+
if (!this._cache) return undefined;
|
|
62
|
+
const cameras = this._cache.cameras || [];
|
|
63
|
+
return cameras.find((c) => c.serialNumber === serialNumber);
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get station config for a specific station by serial number.
|
|
68
|
+
* @param {string} serialNumber
|
|
69
|
+
* @returns {object|undefined}
|
|
70
|
+
*/
|
|
71
|
+
getStationConfig(serialNumber) {
|
|
72
|
+
if (!this._cache) return undefined;
|
|
73
|
+
const stations = this._cache.stations || [];
|
|
74
|
+
return stations.find((s) => s.serialNumber === serialNumber);
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Update or create camera config for a device.
|
|
79
|
+
* @param {string} serialNumber
|
|
80
|
+
* @param {object} options - config properties to merge
|
|
81
|
+
*/
|
|
82
|
+
async updateDeviceConfig(serialNumber, options) {
|
|
83
|
+
const config = this._cache || await this.get();
|
|
84
|
+
if (!config.cameras) config.cameras = [];
|
|
85
|
+
|
|
86
|
+
const idx = config.cameras.findIndex((c) => c.serialNumber === serialNumber);
|
|
87
|
+
if (idx !== -1) {
|
|
88
|
+
Object.assign(config.cameras[idx], options);
|
|
89
|
+
} else {
|
|
90
|
+
config.cameras.push({ serialNumber, ...options });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
await this.update(config);
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Update or create station config.
|
|
98
|
+
* @param {string} serialNumber
|
|
99
|
+
* @param {object} options - config properties to merge
|
|
100
|
+
*/
|
|
101
|
+
async updateStationConfig(serialNumber, options) {
|
|
102
|
+
const config = this._cache || await this.get();
|
|
103
|
+
if (!config.stations) config.stations = [];
|
|
104
|
+
|
|
105
|
+
const idx = config.stations.findIndex((s) => s.serialNumber === serialNumber);
|
|
106
|
+
if (idx !== -1) {
|
|
107
|
+
Object.assign(config.stations[idx], options);
|
|
108
|
+
} else {
|
|
109
|
+
config.stations.push({ serialNumber, ...options });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
await this.update(config);
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Update global config options (top-level properties).
|
|
117
|
+
* @param {object} options - properties to merge into top-level config
|
|
118
|
+
*/
|
|
119
|
+
async updateGlobal(options) {
|
|
120
|
+
const config = this._cache || await this.get();
|
|
121
|
+
Object.assign(config, options);
|
|
122
|
+
await this.update(config);
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Toggle a device in the ignore list.
|
|
127
|
+
* @param {string} serialNumber
|
|
128
|
+
* @param {boolean} ignored - true to ignore, false to un-ignore
|
|
129
|
+
* @param {'device'|'station'} type
|
|
130
|
+
*/
|
|
131
|
+
async toggleIgnore(serialNumber, ignored, type) {
|
|
132
|
+
const config = this._cache || await this.get();
|
|
133
|
+
const key = type === 'device' ? 'ignoreDevices' : 'ignoreStations';
|
|
134
|
+
if (!config[key]) config[key] = [];
|
|
135
|
+
|
|
136
|
+
if (ignored && !config[key].includes(serialNumber)) {
|
|
137
|
+
config[key].push(serialNumber);
|
|
138
|
+
} else if (!ignored) {
|
|
139
|
+
config[key] = config[key].filter((id) => id !== serialNumber);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
await this.update(config);
|
|
143
|
+
},
|
|
144
|
+
};
|