@ubreu/homebridge-eufy-security
Version:
Control Eufy Security from homebridge.
333 lines • 19.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CameraAccessory = void 0;
const Device_1 = require("./Device");
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const eufy_security_client_1 = require("eufy-security-client");
const streamingDelegate_1 = require("../controller/streamingDelegate");
/**
* Platform Accessory
* An instance of this class is created for each accessory your platform registers
* Each accessory may expose multiple services of different service types.
*/
class CameraAccessory extends Device_1.DeviceAccessory {
constructor(platform, accessory, device) {
super(platform, accessory, device);
this.notificationTimeout = null;
this.streamingDelegate = null;
// List of event types
this.eventTypesToHandle = [
'motion detected',
'person detected',
'pet detected',
'vehicle detected',
'sound detected',
'crying detected',
'dog detected',
'stranger person detected',
];
this.cameraConfig = {};
this.cameraStatus = { isEnabled: false, timestamp: 0 }; // Initialize the cameraStatus object
this.platform.log.debug(`${this.accessory.displayName} Constructed Camera`);
this.cameraConfig = this.getCameraConfig();
this.platform.log.debug(`${this.accessory.displayName} config is: ${JSON.stringify(this.cameraConfig)}`);
if (this.cameraConfig.enableCamera || this.device.isDoorbell()) {
this.platform.log.debug(`${this.accessory.displayName} has a camera`);
this.setupCamera();
this.setupChimeButton();
this.initSensorService(this.platform.Service.Battery);
}
else {
this.platform.log.debug(`${this.accessory.displayName} has a motion sensor`);
this.setupMotionFunction();
this.initSensorService(this.platform.Service.MotionSensor);
}
this.setupEnableButton();
this.setupMotionButton();
this.setupLightButton();
this.pruneUnusedServices();
}
setupCamera() {
try {
this.cameraFunction();
const delegate = new streamingDelegate_1.StreamingDelegate(this.platform, this.device, this.cameraConfig, this.platform.api, this.platform.api.hap);
this.streamingDelegate = delegate;
this.accessory.configureController(delegate.controller);
}
catch (error) {
this.platform.log.error(this.accessory.displayName, 'raise error to check and attach livestream function.', error);
}
}
setupButtonService(serviceName, configValue, PropertyName, serviceType) {
try {
this.platform.log.debug(`${this.accessory.displayName} ${serviceName} config:`, configValue);
if (configValue && this.device.hasProperty(PropertyName)) {
// eslint-disable-next-line max-len
this.platform.log.debug(`${this.accessory.displayName} has a ${PropertyName}, so append ${serviceType}${serviceName} characteristic to it.`);
this.setupSwitchService(serviceName, serviceType, PropertyName);
}
else {
// eslint-disable-next-line max-len
this.platform.log.debug(`${this.accessory.displayName} Looks like not compatible with ${PropertyName} or this has been disabled within configuration`);
}
}
catch (error) {
this.platform.log.error(`${this.accessory.displayName} raise error to check and attach ${serviceType}${serviceName}.`, error);
throw Error;
}
}
setupSwitchService(serviceName, serviceType, propertyName) {
const platformServiceMapping = {
switch: this.platform.Service.Switch,
lightbulb: this.platform.Service.Lightbulb,
outlet: this.platform.Service.Outlet,
};
this.registerCharacteristic({
serviceType: platformServiceMapping[serviceType] || this.platform.Service.Switch,
characteristicType: this.platform.Characteristic.On,
name: this.accessory.displayName + '_' + serviceName,
serviceSubType: serviceName,
getValue: (data, characteristic) => this.getCameraPropertyValue(characteristic, propertyName),
setValue: (value, characteristic) => this.setCameraPropertyValue(characteristic, propertyName, value),
});
}
async setupEnableButton() {
this.setupButtonService('Enabled', this.cameraConfig.enableButton, eufy_security_client_1.PropertyName.DeviceEnabled, 'switch');
}
async setupMotionButton() {
this.setupButtonService('Motion', this.cameraConfig.motionButton, eufy_security_client_1.PropertyName.DeviceMotionDetection, 'switch');
}
async setupLightButton() {
this.setupButtonService('Light', true, eufy_security_client_1.PropertyName.DeviceLight, 'lightbulb');
}
async setupChimeButton() {
this.setupButtonService('IndoorChime', this.cameraConfig.indoorChimeButton, eufy_security_client_1.PropertyName.DeviceChimeIndoor, 'switch');
}
getCameraConfig() {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
let config = {};
if (typeof this.platform.config.cameras !== 'undefined') {
// eslint-disable-next-line prefer-arrow-callback, brace-style
const pos = this.platform.config.cameras.map(function (e) { return e.serialNumber; }).indexOf(this.device.getSerial());
config = { ...this.platform.config.cameras[pos] };
}
config.name = this.accessory.displayName;
config.enableButton = (_a = config.enableButton) !== null && _a !== void 0 ? _a : (config.enableButton = true);
config.motionButton = (_b = config.motionButton) !== null && _b !== void 0 ? _b : (config.motionButton = true);
config.rtsp = (_c = config.rtsp) !== null && _c !== void 0 ? _c : (config.rtsp = false);
config.forcerefreshsnap = (_d = config.forcerefreshsnap) !== null && _d !== void 0 ? _d : (config.forcerefreshsnap = false);
config.videoConfig = (_e = config.videoConfig) !== null && _e !== void 0 ? _e : (config.videoConfig = {});
config.useCachedLocalLivestream = (_f = config.useCachedLocalLivestream) !== null && _f !== void 0 ? _f : (config.useCachedLocalLivestream = false);
config.immediateRingNotificationWithoutSnapshot = (_g = config.immediateRingNotificationWithoutSnapshot) !== null && _g !== void 0 ? _g : (config.immediateRingNotificationWithoutSnapshot = false);
config.delayCameraSnapshot = (_h = config.delayCameraSnapshot) !== null && _h !== void 0 ? _h : (config.delayCameraSnapshot = false);
if (!config.snapshotHandlingMethod) {
config.snapshotHandlingMethod = (config.forcerefreshsnap) ? 1 : 3;
}
config.talkback = (_j = config.talkback) !== null && _j !== void 0 ? _j : (config.talkback = false);
if (config.talkback && !this.device.hasCommand(eufy_security_client_1.CommandName.DeviceStartTalkback)) {
this.platform.log.warn(this.accessory.displayName, 'Talkback for this device is not supported!');
config.talkback = false;
}
if (config.talkback && config.rtsp) {
this.platform.log.warn(this.accessory.displayName, 'Talkback cannot be used with rtsp option. Ignoring talkback setting.');
config.talkback = false;
}
return config;
}
cameraFunction() {
this.registerCharacteristic({
serviceType: this.platform.Service.CameraOperatingMode,
characteristicType: this.platform.Characteristic.EventSnapshotsActive,
getValue: (data) => this.handleDummyEventGet('EventSnapshotsActive'),
setValue: (value) => this.handleDummyEventSet('EventSnapshotsActive', value),
});
this.registerCharacteristic({
serviceType: this.platform.Service.CameraOperatingMode,
characteristicType: this.platform.Characteristic.HomeKitCameraActive,
getValue: (data, characteristic) => this.getCameraPropertyValue(characteristic, eufy_security_client_1.PropertyName.DeviceEnabled),
setValue: (value, characteristic) => this.setCameraPropertyValue(characteristic, eufy_security_client_1.PropertyName.DeviceEnabled, value),
});
// Fire snapshot when motion detected
this.registerCharacteristic({
serviceType: this.platform.Service.MotionSensor,
characteristicType: this.platform.Characteristic.MotionDetected,
getValue: (data) => this.device.getPropertyValue(eufy_security_client_1.PropertyName.DeviceMotionDetected),
onValue: (service, characteristic) => {
this.eventTypesToHandle.forEach(eventType => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.device.on(eventType, (device, state) => {
// eslint-disable-next-line max-len
this.platform.log.info(`${this.accessory.displayName} MOTION DETECTED (${eventType})': ${state}`);
characteristic.updateValue(state);
});
});
},
});
if (this.device.hasProperty('enabled')) {
this.registerCharacteristic({
serviceType: this.platform.Service.CameraOperatingMode,
characteristicType: this.platform.Characteristic.ManuallyDisabled,
getValue: (data, characteristic) => this.getCameraPropertyValue(characteristic, eufy_security_client_1.PropertyName.DeviceEnabled),
});
}
if (this.device.hasProperty('statusLed')) {
this.registerCharacteristic({
serviceType: this.platform.Service.CameraOperatingMode,
characteristicType: this.platform.Characteristic.CameraOperatingModeIndicator,
getValue: (data, characteristic) => this.getCameraPropertyValue(characteristic, eufy_security_client_1.PropertyName.DeviceStatusLed),
setValue: (value, characteristic) => this.setCameraPropertyValue(characteristic, eufy_security_client_1.PropertyName.DeviceStatusLed, value),
});
}
if (this.device.hasProperty('nightvision')) {
this.registerCharacteristic({
serviceType: this.platform.Service.CameraOperatingMode,
characteristicType: this.platform.Characteristic.NightVision,
getValue: (data, characteristic) => this.getCameraPropertyValue(characteristic, eufy_security_client_1.PropertyName.DeviceNightvision),
setValue: (value, characteristic) => this.setCameraPropertyValue(characteristic, eufy_security_client_1.PropertyName.DeviceNightvision, value),
});
}
if (this.device.hasProperty('autoNightvision')) {
this.registerCharacteristic({
serviceType: this.platform.Service.CameraOperatingMode,
characteristicType: this.platform.Characteristic.NightVision,
getValue: (data, characteristic) => this.getCameraPropertyValue(characteristic, eufy_security_client_1.PropertyName.DeviceAutoNightvision),
setValue: (value, characteristic) => this.setCameraPropertyValue(characteristic, eufy_security_client_1.PropertyName.DeviceAutoNightvision, value),
});
}
// if (this.device.hasProperty('speaker')) {
// this.registerCharacteristic({
// serviceType: this.platform.Service.Speaker,
// characteristicType: this.platform.Characteristic.Mute,
// serviceSubType: 'speaker_mute',
// getValue: (data, characteristic) =>
// this.getCameraPropertyValue(characteristic, PropertyName.DeviceSpeaker),
// setValue: (value, characteristic) =>
// this.setCameraPropertyValue(characteristic, PropertyName.DeviceSpeaker, value),
// });
// }
// if (this.device.hasProperty('speakerVolume')) {
// this.registerCharacteristic({
// serviceType: this.platform.Service.Speaker,
// characteristicType: this.platform.Characteristic.Volume,
// serviceSubType: 'speaker_volume',
// getValue: (data, characteristic) =>
// this.getCameraPropertyValue(characteristic, PropertyName.DeviceSpeakerVolume),
// setValue: (value, characteristic) =>
// this.setCameraPropertyValue(characteristic, PropertyName.DeviceSpeakerVolume, value),
// });
// }
// if (this.device.hasProperty('microphone')) {
// this.registerCharacteristic({
// serviceType: this.platform.Service.Microphone,
// characteristicType: this.platform.Characteristic.Mute,
// serviceSubType: 'mic_mute',
// getValue: (data, characteristic) =>
// this.getCameraPropertyValue(characteristic, PropertyName.DeviceMicrophone),
// setValue: (value, characteristic) =>
// this.setCameraPropertyValue(characteristic, PropertyName.DeviceMicrophone, value),
// });
// }
if (this.device.isDoorbell()) {
this.registerCharacteristic({
serviceType: this.platform.Service.Doorbell,
characteristicType: this.platform.Characteristic.ProgrammableSwitchEvent,
getValue: () => this.handleDummyEventGet('EventSnapshotsActive'),
onValue: (service, characteristic) => {
this.device.on('rings', (device, state) => this.onDeviceRingsPushNotification(characteristic));
},
});
}
this.getService(this.platform.Service.CameraOperatingMode).setPrimaryService(true);
}
// This private function sets up the motion sensor characteristics for the accessory.
setupMotionFunction() {
// Register the motion sensor characteristic for detecting motion.
this.registerCharacteristic({
serviceType: this.platform.Service.MotionSensor,
characteristicType: this.platform.Characteristic.MotionDetected,
getValue: (data) => this.device.getPropertyValue(eufy_security_client_1.PropertyName.DeviceMotionDetected),
onMultipleValue: this.eventTypesToHandle,
});
// If the camera is disabled, flag the motion sensor as tampered.
// This is done because the motion sensor won't work until the camera is enabled again.
this.registerCharacteristic({
serviceType: this.platform.Service.MotionSensor,
characteristicType: this.platform.Characteristic.StatusTampered,
getValue: (data) => {
const tampered = this.device.getPropertyValue(eufy_security_client_1.PropertyName.DeviceEnabled);
this.platform.log.debug(`${this.accessory.displayName} TAMPERED? ${!tampered}`);
return tampered
? this.platform.Characteristic.StatusTampered.NOT_TAMPERED
: this.platform.Characteristic.StatusTampered.TAMPERED;
},
});
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
getCameraPropertyValue(characteristic, propertyName) {
try {
let value = this.device.getPropertyValue(propertyName);
this.platform.log.debug(`${this.accessory.displayName} GET '${characteristic.displayName}' ${propertyName}: ${value}`);
if (propertyName === eufy_security_client_1.PropertyName.DeviceNightvision) {
return value === 1;
}
// Override for PropertyName.DeviceEnabled when enabled button is fired and
if (propertyName === eufy_security_client_1.PropertyName.DeviceEnabled &&
Date.now() - this.cameraStatus.timestamp <= 60000) {
// eslint-disable-next-line max-len
this.platform.log.debug(`${this.accessory.displayName} CACHED for (1 min) '${characteristic.displayName}' ${propertyName}: ${this.cameraStatus.isEnabled}`);
value = this.cameraStatus.isEnabled;
}
if (characteristic.displayName === 'Manually Disabled') {
value = !value;
this.platform.log.debug(`${this.accessory.displayName} INVERSED '${characteristic.displayName}' ${propertyName}: ${value}`);
}
if (value === undefined) {
return false;
}
return value;
}
catch (error) {
this.platform.log.debug(`${this.accessory.displayName} Error getting '${characteristic.displayName}' ${propertyName}: ${error}`);
return false;
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async setCameraPropertyValue(characteristic, propertyName, value) {
try {
this.platform.log.debug(`${this.accessory.displayName} SET '${characteristic.displayName}' ${propertyName}: ${value}`);
await this.setPropertyValue(propertyName, value);
if (propertyName === eufy_security_client_1.PropertyName.DeviceEnabled &&
characteristic.displayName === 'On') {
characteristic.updateValue(value);
this.cameraStatus = { isEnabled: value, timestamp: Date.now() };
characteristic = this.getService(this.platform.Service.CameraOperatingMode)
.getCharacteristic(this.platform.Characteristic.ManuallyDisabled);
this.platform.log.debug(`${this.accessory.displayName} INVERSED '${characteristic.displayName}' ${propertyName}: ${!value}`);
value = !value;
}
characteristic.updateValue(value);
}
catch (error) {
this.platform.log.debug(`${this.accessory.displayName} Error setting '${characteristic.displayName}' ${propertyName}: ${error}`);
}
}
/**
* Handle push notifications for a doorbell device.
* Mute subsequent notifications within a timeout period.
* @param characteristic - The Characteristic to update for HomeKit.
*/
onDeviceRingsPushNotification(characteristic) {
if (!this.notificationTimeout) {
this.platform.log.debug(`${this.accessory.displayName} DoorBell ringing`);
if (this.cameraConfig.useCachedLocalLivestream && this.streamingDelegate) {
this.streamingDelegate.prepareCachedStream();
}
characteristic.updateValue(this.platform.Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS);
// Set a new timeout for muting subsequent notifications
this.notificationTimeout = setTimeout(() => { }, 3000);
}
}
}
exports.CameraAccessory = CameraAccessory;
//# sourceMappingURL=CameraAccessory.js.map