@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,142 @@
|
|
|
1
|
+
import { DeviceAccessory } from './Device.js';
|
|
2
|
+
import { PropertyName } from 'eufy-security-client';
|
|
3
|
+
import { CHAR, SERV } from '../utils/utils.js';
|
|
4
|
+
/**
|
|
5
|
+
* LockAccessory Class
|
|
6
|
+
*
|
|
7
|
+
* This class represents a lock accessory within a home automation system. It is designed to
|
|
8
|
+
* integrate smart locks into the system, register appropriate HomeKit characteristics, and provide
|
|
9
|
+
* functionality for controlling and monitoring the lock's status.
|
|
10
|
+
*
|
|
11
|
+
* @class LockAccessory
|
|
12
|
+
* @extends DeviceAccessory
|
|
13
|
+
*/
|
|
14
|
+
export class LockAccessory extends DeviceAccessory {
|
|
15
|
+
/**
|
|
16
|
+
* Constructor for LockAccessory.
|
|
17
|
+
*
|
|
18
|
+
* @param {EufySecurityPlatform} platform - The platform instance managing accessories.
|
|
19
|
+
* @param {PlatformAccessory} accessory - The platform-specific accessory.
|
|
20
|
+
* @param {Lock} device - The lock device being represented.
|
|
21
|
+
*/
|
|
22
|
+
constructor(platform, accessory, device) {
|
|
23
|
+
// Call the constructor of the parent class DeviceAccessory.
|
|
24
|
+
super(platform, accessory, device);
|
|
25
|
+
// Log that the LockAccessory is constructed.
|
|
26
|
+
this.log.debug(`Constructed Lock`);
|
|
27
|
+
// Check if the device has the 'locked' property.
|
|
28
|
+
if (this.device.hasProperty('locked')) {
|
|
29
|
+
// Initialize Lock Management Service characteristics.
|
|
30
|
+
this.initLockManagementService();
|
|
31
|
+
// Initialize Lock Mechanism Service characteristics.
|
|
32
|
+
this.initLockMechanismService();
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// Log an error if the device has no lock.
|
|
36
|
+
this.log.error(`has no lock`);
|
|
37
|
+
}
|
|
38
|
+
// Prune any unused services.
|
|
39
|
+
this.pruneUnusedServices();
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Initializes characteristics for the Lock Management Service.
|
|
43
|
+
*/
|
|
44
|
+
initLockManagementService() {
|
|
45
|
+
// Register Lock Management Service characteristics.
|
|
46
|
+
// Version characteristic (always returns '1.0').
|
|
47
|
+
this.registerCharacteristic({
|
|
48
|
+
serviceType: SERV.LockManagement,
|
|
49
|
+
characteristicType: CHAR.Version,
|
|
50
|
+
getValue: () => '1.0',
|
|
51
|
+
});
|
|
52
|
+
// LockManagementAutoSecurityTimeout characteristic (always returns 3 seconds).
|
|
53
|
+
this.registerCharacteristic({
|
|
54
|
+
serviceType: SERV.LockManagement,
|
|
55
|
+
characteristicType: CHAR.LockManagementAutoSecurityTimeout,
|
|
56
|
+
getValue: () => 3,
|
|
57
|
+
});
|
|
58
|
+
// AdministratorOnlyAccess characteristic (always returns true).
|
|
59
|
+
this.registerCharacteristic({
|
|
60
|
+
serviceType: SERV.LockManagement,
|
|
61
|
+
characteristicType: CHAR.AdministratorOnlyAccess,
|
|
62
|
+
getValue: () => true,
|
|
63
|
+
});
|
|
64
|
+
// LockControlPoint characteristic (no initial value).
|
|
65
|
+
this.registerCharacteristic({
|
|
66
|
+
serviceType: SERV.LockManagement,
|
|
67
|
+
characteristicType: CHAR.LockControlPoint,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Initializes characteristics for the Lock Mechanism Service.
|
|
72
|
+
*/
|
|
73
|
+
initLockMechanismService() {
|
|
74
|
+
// Register Lock Mechanism Service characteristics.
|
|
75
|
+
// LockCurrentState and LockTargetState characteristics.
|
|
76
|
+
this.registerCharacteristic({
|
|
77
|
+
serviceType: SERV.LockMechanism,
|
|
78
|
+
characteristicType: CHAR.LockCurrentState,
|
|
79
|
+
getValue: () => this.getLockStatus(),
|
|
80
|
+
onValue: (service, characteristic) => {
|
|
81
|
+
this.device.on('locked', () => {
|
|
82
|
+
characteristic.updateValue(this.getLockStatus());
|
|
83
|
+
});
|
|
84
|
+
this.device.on('jammed', () => {
|
|
85
|
+
characteristic.updateValue(CHAR.LockCurrentState.JAMMED);
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
this.registerCharacteristic({
|
|
90
|
+
serviceType: SERV.LockMechanism,
|
|
91
|
+
characteristicType: CHAR.LockTargetState,
|
|
92
|
+
getValue: () => this.getLockStatus(),
|
|
93
|
+
setValue: async (value) => {
|
|
94
|
+
try {
|
|
95
|
+
await this.setLockTargetState(value);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
this.log.error(`Lock target state could not be set: ${error}`);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
onValue: (service, characteristic) => {
|
|
102
|
+
this.device.on('locked', () => {
|
|
103
|
+
characteristic.updateValue(this.getLockStatus());
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
// Initialize the sensor service.
|
|
108
|
+
this.initSensorService();
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Gets the lock status and maps it to HomeKit lock states.
|
|
112
|
+
*/
|
|
113
|
+
getLockStatus() {
|
|
114
|
+
const lockStatus = this.device.isLocked();
|
|
115
|
+
this.log.debug(`getLockStatus: ${lockStatus}`);
|
|
116
|
+
return this.convertLockStatusCode(lockStatus);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Sets the lock target state asynchronously.
|
|
120
|
+
*/
|
|
121
|
+
async setLockTargetState(state) {
|
|
122
|
+
try {
|
|
123
|
+
await this.setPropertyValue(PropertyName.DeviceLocked, !!state);
|
|
124
|
+
this.log.info(`Lock target state set to: ${state}`);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
this.log.error(`Error setting lock target state: ${error}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Converts lock status codes to corresponding HomeKit lock states.
|
|
132
|
+
*/
|
|
133
|
+
convertLockStatusCode(lockStatus) {
|
|
134
|
+
if (lockStatus) {
|
|
135
|
+
return CHAR.LockCurrentState.SECURED;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
return CHAR.LockCurrentState.UNSECURED;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=LockAccessory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LockAccessory.js","sourceRoot":"","sources":["../../src/accessories/LockAccessory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { DeviceAccessory } from './Device.js';
|
|
2
|
+
// @ts-ignore
|
|
3
|
+
import { PropertyName } from 'eufy-security-client';
|
|
4
|
+
import { CHAR, SERV } from '../utils/utils.js';
|
|
5
|
+
/**
|
|
6
|
+
* MotionSensorAccessory Class
|
|
7
|
+
*
|
|
8
|
+
* This class represents a motion sensor accessory within a home automation system. It is designed
|
|
9
|
+
* to integrate motion sensors into the system, register appropriate characteristics, and provide
|
|
10
|
+
* necessary functionality for motion detection.
|
|
11
|
+
*
|
|
12
|
+
* @class MotionSensorAccessory
|
|
13
|
+
* @extends DeviceAccessory
|
|
14
|
+
*/
|
|
15
|
+
export class MotionSensorAccessory extends DeviceAccessory {
|
|
16
|
+
/**
|
|
17
|
+
* Constructor for MotionSensorAccessory.
|
|
18
|
+
*
|
|
19
|
+
* @param {EufySecurityPlatform} platform - The platform instance managing accessories.
|
|
20
|
+
* @param {PlatformAccessory} accessory - The platform-specific accessory.
|
|
21
|
+
* @param {MotionSensor} device - The motion sensor device being represented.
|
|
22
|
+
*/
|
|
23
|
+
constructor(platform, accessory, device) {
|
|
24
|
+
// Call the constructor of the parent class DeviceAccessory.
|
|
25
|
+
super(platform, accessory, device);
|
|
26
|
+
// Log a debug message indicating the construction of the Motion Sensor.
|
|
27
|
+
this.log.debug(`Constructed Motion Sensor`);
|
|
28
|
+
// Check if the device has the 'motionDetected' property.
|
|
29
|
+
if (this.device.hasProperty('motionDetected')) {
|
|
30
|
+
// Register the Motion Detected characteristic.
|
|
31
|
+
this.registerCharacteristic({
|
|
32
|
+
serviceType: SERV.MotionSensor,
|
|
33
|
+
characteristicType: CHAR.MotionDetected,
|
|
34
|
+
getValue: () => this.device.getPropertyValue(PropertyName.DeviceMotionDetected),
|
|
35
|
+
onSimpleValue: 'motion detected',
|
|
36
|
+
});
|
|
37
|
+
// Initialize the sensor service.
|
|
38
|
+
this.initSensorService();
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// Log an error if the 'motionDetected' property is not available for this device.
|
|
42
|
+
this.log.error(`has no motionDetected`);
|
|
43
|
+
}
|
|
44
|
+
// Remove any unused services.
|
|
45
|
+
this.pruneUnusedServices();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=MotionSensorAccessory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MotionSensorAccessory.js","sourceRoot":"","sources":["../../src/accessories/MotionSensorAccessory.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,eAAe;AACf,OAAO,EAAgB,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE/C;;;;;;;;;GASG;AACH,MAAM,OAAO,qBAAsB,SAAQ,eAAe;IAExD;;;;;;OAMG;IACH,YACE,QAA8B,EAC9B,SAA4B,EAC5B,MAAoB;QAEpB,4DAA4D;QAC5D,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QAEnC,wEAAwE;QACxE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAE5C,yDAAyD;QACzD,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAE9C,+CAA+C;YAC/C,IAAI,CAAC,sBAAsB,CAAC;gBAC1B,WAAW,EAAE,IAAI,CAAC,YAAY;gBAC9B,kBAAkB,EAAE,IAAI,CAAC,cAAc;gBACvC,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,YAAY,CAAC,oBAAoB,CAAC;gBAC/E,aAAa,EAAE,iBAAiB;aACjC,CAAC,CAAC;YAEH,iCAAiC;YACjC,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAE3B,CAAC;aAAM,CAAC;YACN,kFAAkF;YAClF,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC1C,CAAC;QAED,8BAA8B;QAC9B,IAAI,CAAC,mBAAmB,EAAE,CAAC;IAC7B,CAAC;CACF"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { DeviceAccessory } from './Device.js';
|
|
2
|
+
import { PropertyName } from 'eufy-security-client';
|
|
3
|
+
import { CHAR, SERV } from '../utils/utils.js';
|
|
4
|
+
/**
|
|
5
|
+
* SmartDropAccessory Class
|
|
6
|
+
*
|
|
7
|
+
* This class represents a SmartDrop accessory within a home automation system.
|
|
8
|
+
* The SmartDrop is a smart package delivery box that can be remotely opened.
|
|
9
|
+
* It is exposed as a LockMechanism service in HomeKit, allowing users to
|
|
10
|
+
* open the SmartDrop lid from the Home app or via automation.
|
|
11
|
+
*
|
|
12
|
+
* Since the SmartDrop is a one-way latch (open only, closing is physical),
|
|
13
|
+
* the lock mechanism is used to trigger the open command, and the current
|
|
14
|
+
* state reflects whether the lid is currently open or closed.
|
|
15
|
+
*
|
|
16
|
+
* @class SmartDropAccessory
|
|
17
|
+
* @extends DeviceAccessory
|
|
18
|
+
* @see https://github.com/homebridge-plugins/homebridge-eufy-security/issues/770
|
|
19
|
+
*/
|
|
20
|
+
export class SmartDropAccessory extends DeviceAccessory {
|
|
21
|
+
/**
|
|
22
|
+
* Constructor for SmartDropAccessory.
|
|
23
|
+
*
|
|
24
|
+
* @param {EufySecurityPlatform} platform - The platform instance managing accessories.
|
|
25
|
+
* @param {PlatformAccessory} accessory - The platform-specific accessory.
|
|
26
|
+
* @param {SmartDrop} device - The SmartDrop device being represented.
|
|
27
|
+
*/
|
|
28
|
+
constructor(platform, accessory, device) {
|
|
29
|
+
super(platform, accessory, device);
|
|
30
|
+
this.log.debug(`Constructed SmartDrop`);
|
|
31
|
+
// Initialize the Lock Mechanism service for open/close control.
|
|
32
|
+
this.initLockMechanismService();
|
|
33
|
+
// Initialize the Contact Sensor service for package delivery detection.
|
|
34
|
+
this.initContactSensorService();
|
|
35
|
+
// Initialize the battery service.
|
|
36
|
+
this.initSensorService();
|
|
37
|
+
// Prune any unused services.
|
|
38
|
+
this.pruneUnusedServices();
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Initializes the Lock Mechanism Service.
|
|
42
|
+
*
|
|
43
|
+
* The SmartDrop lid is modeled as a lock:
|
|
44
|
+
* - SECURED = lid closed
|
|
45
|
+
* - UNSECURED = lid open
|
|
46
|
+
* Setting the target state to UNSECURED triggers the open command.
|
|
47
|
+
*/
|
|
48
|
+
initLockMechanismService() {
|
|
49
|
+
// LockCurrentState reflects the actual open/closed state of the lid.
|
|
50
|
+
this.registerCharacteristic({
|
|
51
|
+
serviceType: SERV.LockMechanism,
|
|
52
|
+
characteristicType: CHAR.LockCurrentState,
|
|
53
|
+
getValue: () => this.getLidStatus(),
|
|
54
|
+
onValue: (service, characteristic) => {
|
|
55
|
+
this.device.on('open', () => {
|
|
56
|
+
characteristic.updateValue(this.getLidStatus());
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
// LockTargetState allows triggering the open command.
|
|
61
|
+
this.registerCharacteristic({
|
|
62
|
+
serviceType: SERV.LockMechanism,
|
|
63
|
+
characteristicType: CHAR.LockTargetState,
|
|
64
|
+
getValue: () => this.getLidStatus(),
|
|
65
|
+
setValue: async (value) => {
|
|
66
|
+
await this.setLidTargetState(value);
|
|
67
|
+
},
|
|
68
|
+
onValue: (service, characteristic) => {
|
|
69
|
+
this.device.on('open', () => {
|
|
70
|
+
characteristic.updateValue(this.getLidStatus());
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Initializes the Contact Sensor Service.
|
|
77
|
+
*
|
|
78
|
+
* Exposes a contact sensor that indicates whether a package has been delivered.
|
|
79
|
+
* - CONTACT_DETECTED = no package delivered (normal state)
|
|
80
|
+
* - CONTACT_NOT_DETECTED = package delivered (alert state)
|
|
81
|
+
*/
|
|
82
|
+
initContactSensorService() {
|
|
83
|
+
if (!this.device.hasProperty('packageDelivered')) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.registerCharacteristic({
|
|
87
|
+
serviceType: SERV.ContactSensor,
|
|
88
|
+
characteristicType: CHAR.ContactSensorState,
|
|
89
|
+
name: 'Package Delivered',
|
|
90
|
+
getValue: () => this.getPackageDeliveredStatus(),
|
|
91
|
+
onSimpleValue: 'package delivered',
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Gets the lid status and maps it to HomeKit lock states.
|
|
96
|
+
*
|
|
97
|
+
* @returns {CharacteristicValue} UNSECURED if open, SECURED if closed.
|
|
98
|
+
*/
|
|
99
|
+
getLidStatus() {
|
|
100
|
+
if (this.device.hasProperty('open')) {
|
|
101
|
+
const isOpen = this.device.getPropertyValue(PropertyName.DeviceOpen);
|
|
102
|
+
this.log.debug(`getLidStatus: ${isOpen}`);
|
|
103
|
+
return isOpen
|
|
104
|
+
? CHAR.LockCurrentState.UNSECURED
|
|
105
|
+
: CHAR.LockCurrentState.SECURED;
|
|
106
|
+
}
|
|
107
|
+
return CHAR.LockCurrentState.SECURED;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Sets the lid target state.
|
|
111
|
+
* Only the UNSECURED (open) action is meaningful for SmartDrop.
|
|
112
|
+
* When set to UNSECURED, it sends the open command to the station.
|
|
113
|
+
* Setting to SECURED is ignored as closing is a physical action.
|
|
114
|
+
*
|
|
115
|
+
* @param {CharacteristicValue} state - The target lock state.
|
|
116
|
+
*/
|
|
117
|
+
async setLidTargetState(state) {
|
|
118
|
+
if (state === CHAR.LockTargetState.UNSECURED) {
|
|
119
|
+
try {
|
|
120
|
+
this.log.info(`Opening SmartDrop lid...`);
|
|
121
|
+
const station = await this.platform.eufyClient.getStation(this.device.getStationSerial());
|
|
122
|
+
station.open(this.device);
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
this.log.error(`SmartDrop lid could not be opened: ${error}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
this.log.debug(`SmartDrop lid closing is a physical action, ignoring SECURED command.`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Gets the package delivered status for the contact sensor.
|
|
134
|
+
*
|
|
135
|
+
* @returns {CharacteristicValue} CONTACT_NOT_DETECTED if package delivered, CONTACT_DETECTED otherwise.
|
|
136
|
+
*/
|
|
137
|
+
getPackageDeliveredStatus() {
|
|
138
|
+
const delivered = this.device.getPropertyValue(PropertyName.DevicePackageDelivered);
|
|
139
|
+
this.log.debug(`getPackageDeliveredStatus: ${delivered}`);
|
|
140
|
+
return delivered
|
|
141
|
+
? CHAR.ContactSensorState.CONTACT_NOT_DETECTED
|
|
142
|
+
: CHAR.ContactSensorState.CONTACT_DETECTED;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=SmartDropAccessory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SmartDropAccessory.js","sourceRoot":"","sources":["../../src/accessories/SmartDropAccessory.ts"],"names":[],"mappings":"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"}
|