homebridge-ring
Version:
Homebridge plugin for Ring doorbells, cameras, security alarm system and smart lighting
138 lines (137 loc) • 5.78 kB
JavaScript
import { BaseDeviceAccessory } from "./base-device-accessory.js";
import { allAlarmStates, RingDeviceType } from 'ring-client-api';
import { hap } from "./hap.js";
import { logError, logInfo } from 'ring-client-api/util';
function isValidNightModeBypass(mode) {
return mode && (mode === 'all' || mode === 'some');
}
export class SecurityPanel extends BaseDeviceAccessory {
alarmStates;
device;
accessory;
config;
constructor(device, accessory, config) {
super();
this.device = device;
this.accessory = accessory;
this.config = config;
this.alarmStates = this.config.alarmOnEntryDelay
? allAlarmStates
: allAlarmStates.filter((x) => x !== 'entry-delay');
const { Characteristic, Service } = hap, validValues = [
Characteristic.SecuritySystemTargetState.AWAY_ARM,
Characteristic.SecuritySystemTargetState.STAY_ARM,
Characteristic.SecuritySystemTargetState.DISARM,
];
if (isValidNightModeBypass(config.nightModeBypassFor)) {
validValues.push(Characteristic.SecuritySystemTargetState.NIGHT_ARM);
}
this.registerCharacteristic({
characteristicType: Characteristic.SecuritySystemCurrentState,
serviceType: Service.SecuritySystem,
getValue: (data) => this.getCurrentState(data),
});
this.registerCharacteristic({
characteristicType: Characteristic.SecuritySystemTargetState,
serviceType: Service.SecuritySystem,
getValue: (data) => this.getTargetState(data),
setValue: (value) => this.setTargetState(value),
});
this.getService(Service.SecuritySystem)
.getCharacteristic(Characteristic.SecuritySystemTargetState)
.setProps({ validValues });
if (!config.hideAlarmSirenSwitch) {
this.registerCharacteristic({
characteristicType: Characteristic.On,
serviceType: Service.Switch,
name: this.device.name + ' Siren',
getValue: (data) => data.siren && data.siren.state === 'on',
setValue: (value) => {
if (value) {
return this.device.location.soundSiren();
}
return this.device.location.silenceSiren();
},
});
}
this.getService(Service.SecuritySystem).setPrimaryService(true);
}
getTargetNightMode() {
return this.targetingNightMode && this.config.nightModeBypassFor;
}
getTargetState({ mode }) {
const { Characteristic: { SecuritySystemTargetState: State }, } = hap;
if (mode === this.getTargetNightMode()) {
return State.NIGHT_ARM;
}
// current mode does not match night mode target, so we are no longer targeting night mode
this.targetingNightMode = false;
switch (mode) {
case 'all':
return State.AWAY_ARM;
case 'some':
return State.STAY_ARM;
case 'none':
return State.DISARM;
default:
return State.DISARM;
}
}
getCurrentState(data) {
const { alarmInfo } = data;
if (alarmInfo && this.alarmStates.includes(alarmInfo.state)) {
return hap.Characteristic.SecuritySystemCurrentState.ALARM_TRIGGERED;
}
return this.getTargetState(data);
}
get targetingNightMode() {
return this.accessory.context.targetingNightMode;
}
set targetingNightMode(value) {
this.accessory.context.targetingNightMode = value;
}
async setTargetState(state) {
const { Characteristic: { SecuritySystemTargetState: State }, } = hap, { location } = this.device, { nightModeBypassFor } = this.config;
let bypass = false;
this.targetingNightMode = state === State.NIGHT_ARM;
if (state === State.NIGHT_ARM) {
if (nightModeBypassFor &&
(nightModeBypassFor === 'all' || nightModeBypassFor === 'some')) {
state = nightModeBypassFor === 'all' ? State.AWAY_ARM : State.STAY_ARM;
bypass = true;
}
else {
// Switch to Home since we don't know which mode the user wanted
state = State.STAY_ARM;
}
}
const bypassContactSensors = bypass
? (await location.getDevices()).filter((device) => {
return (device.deviceType === RingDeviceType.ContactSensor &&
device.data.faulted);
})
: [], bypassSensorZids = bypassContactSensors.map((sensor) => sensor.id), bypassSensorNames = bypassContactSensors.map((sensor) => sensor.name), bypassLog = bypassSensorNames.length
? ' - Bypassing Sensors: ' + bypassSensorNames.join(', ')
: '';
try {
if (state === State.AWAY_ARM) {
logInfo(`Arming (Away) ${this.device.name}${bypassLog}`);
await location.armAway(bypassSensorZids);
}
else if (state === State.DISARM) {
logInfo(`Disarming ${this.device.name}`);
await location.disarm();
}
else {
logInfo(`Arming (Home) ${this.device.name}${bypassLog}`);
await location.armHome(bypassSensorZids);
}
}
catch (e) {
logError(e);
this.getService(hap.Service.SecuritySystem)
.getCharacteristic(hap.Characteristic.SecuritySystemTargetState)
.updateValue(this.getTargetState(this.device.data));
}
}
}