UNPKG

@koush/ring-client-api

Version:

Unofficial API for Ring doorbells, cameras, security alarm system and smart lighting

204 lines (203 loc) 10.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Camera = void 0; const hap_1 = require("./hap"); const base_data_accessory_1 = require("./base-data-accessory"); const operators_1 = require("rxjs/operators"); const camera_source_1 = require("./camera-source"); const rxjs_1 = require("rxjs"); const target_value_timer_1 = require("./target-value-timer"); class Camera extends base_data_accessory_1.BaseDataAccessory { constructor(device, accessory, logger, config) { super(); this.device = device; this.accessory = accessory; this.logger = logger; this.config = config; this.cameraSource = new camera_source_1.CameraSource(this.device); if (!hap_1.hap.CameraController) { const error = 'HAP CameraController not found. Please make sure you are on homebridge version 1.0.0 or newer'; logger.error(error); throw new Error(error); } const { Characteristic, Service } = hap_1.hap, { ChargingState, StatusLowBattery } = Characteristic; accessory.configureController(this.cameraSource.controller); this.registerCharacteristic({ characteristicType: Characteristic.Mute, serviceType: Service.Microphone, getValue: () => false, }); this.registerCharacteristic({ characteristicType: Characteristic.Mute, serviceType: Service.Speaker, getValue: () => false, }); if (!config.hideCameraMotionSensor) { this.registerObservableCharacteristic({ characteristicType: Characteristic.MotionDetected, serviceType: Service.MotionSensor, onValue: device.onMotionDetected.pipe((0, operators_1.switchMap)((motion) => { if (!motion) { return Promise.resolve(false); } return this.loadSnapshotForEvent('Detected Motion', true); })), }); } if (device.isDoorbot) { const onDoorbellPressed = device.onDoorbellPressed.pipe((0, operators_1.mapTo)('Doorbell Pressed')), onEventDescription = config.sendDoorbellMotionNotificationsToTv ? (0, rxjs_1.merge)(onDoorbellPressed, device.onMotionStarted.pipe((0, operators_1.mapTo)('Motion Detected - Simulating Doorbell Press'))) : onDoorbellPressed; this.registerObservableCharacteristic({ characteristicType: Characteristic.ProgrammableSwitchEvent, serviceType: Service.Doorbell, onValue: onEventDescription.pipe((0, operators_1.switchMap)((eventDescription) => { return this.loadSnapshotForEvent(eventDescription, Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); })), }); if (!config.hideDoorbellSwitch) { this.registerObservableCharacteristic({ characteristicType: Characteristic.ProgrammableSwitchEvent, serviceType: Service.StatelessProgrammableSwitch, onValue: device.onDoorbellPressed.pipe((0, operators_1.mapTo)(Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS)), }); // Hide long and double press events by setting max value this.getService(Service.StatelessProgrammableSwitch) .getCharacteristic(Characteristic.ProgrammableSwitchEvent) .setProps({ maxValue: Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS, }); } } else if (config.sendCameraMotionNotificationsToTv) { // allow standalone cameras to act as a doorbell press when motion is detected // this allows tvOS 14 notifications to show camera motion alerts this.registerObservableCharacteristic({ characteristicType: Characteristic.ProgrammableSwitchEvent, serviceType: Service.Doorbell, onValue: device.onMotionStarted.pipe((0, operators_1.switchMap)(() => { return this.loadSnapshotForEvent('Motion Detected - Simulating Doorbell Press', Characteristic.ProgrammableSwitchEvent.SINGLE_PRESS); })), }); } if (device.hasLight && !config.hideCameraLight) { const lightTargetTimer = new target_value_timer_1.TargetValueTimer(); this.registerCharacteristic({ characteristicType: Characteristic.On, serviceType: Service.Lightbulb, getValue: (data) => { const value = lightTargetTimer.hasTarget() ? lightTargetTimer.getTarget() : data.led_status === 'on'; return value; }, setValue: (value) => { // Allow 5 seconds for the light value to update in our status updates from Ring lightTargetTimer.setTarget(value, 5000); return device.setLight(value); }, requestUpdate: () => device.requestUpdate(), }); } if (device.hasSiren && !config.hideCameraSirenSwitch) { this.registerCharacteristic({ characteristicType: Characteristic.On, serviceType: Service.Switch, serviceSubType: 'Siren', name: device.name + ' Siren', getValue: (data) => { return Boolean(data.siren_status && data.siren_status.seconds_remaining); }, setValue: (value) => device.setSiren(value), requestUpdate: () => device.requestUpdate(), }); } if (device.hasInHomeDoorbell && !config.hideInHomeDoorbellSwitch) { this.device.onInHomeDoorbellStatus.subscribe((data) => { this.inHomeDoorbellStatus = data; }); this.registerObservableCharacteristic({ characteristicType: Characteristic.On, serviceType: Service.Switch, serviceSubType: 'In-Home Doorbell', name: device.name + ' In-Home Doorbell', onValue: device.onInHomeDoorbellStatus, setValue: (value) => device.setInHomeDoorbell(value), requestUpdate: () => device.requestUpdate(), }); } this.registerCharacteristic({ characteristicType: Characteristic.Manufacturer, serviceType: Service.AccessoryInformation, getValue: () => 'Ring', }); this.registerCharacteristic({ characteristicType: Characteristic.Model, serviceType: Service.AccessoryInformation, getValue: (data) => `${device.model} (${data.kind})`, }); this.registerCharacteristic({ characteristicType: Characteristic.SerialNumber, serviceType: Service.AccessoryInformation, getValue: (data) => data.device_id, }); if (device.hasBattery || // detected as having battery in the past accessory.getService(Service.BatteryService)) { this.registerCharacteristic({ characteristicType: Characteristic.StatusLowBattery, serviceType: Service.BatteryService, getValue: () => { return device.hasLowBattery ? StatusLowBattery.BATTERY_LEVEL_LOW : StatusLowBattery.BATTERY_LEVEL_NORMAL; }, }); this.registerCharacteristic({ characteristicType: Characteristic.ChargingState, serviceType: Service.BatteryService, getValue: (data) => { return data.external_connection ? ChargingState.CHARGING : ChargingState.NOT_CHARGING; }, }); this.registerObservableCharacteristic({ characteristicType: Characteristic.BatteryLevel, serviceType: Service.BatteryService, onValue: device.onBatteryLevel.pipe((0, operators_1.map)((batteryLevel) => { return batteryLevel === null ? 100 : batteryLevel; })), }); } } loadSnapshotForEvent(eventDescription, characteristicValue) { return __awaiter(this, void 0, void 0, function* () { if (this.device.operatingOnBattery) { // battery cameras cannot fetch a new snapshot while recording is in progress this.logger.info(this.device.name + ' ' + eventDescription); return characteristicValue; } this.logger.info(this.device.name + ` ${eventDescription}. Loading snapshot before sending event to HomeKit`); try { yield this.cameraSource.loadSnapshot(); } catch (e) { this.logger.info(this.device.name + ' Failed to load snapshot. Sending event to HomeKit without new snapshot'); } return characteristicValue; }); } } exports.Camera = Camera;