Skip to content
This repository has been archived by the owner on Jun 23, 2022. It is now read-only.

Commit

Permalink
Add support for alarm_control_panel (Fixes #19) (#176)
Browse files Browse the repository at this point in the history
* Update README.md

Add 'homebridge_gas_type" description.

* Update binary_sensor.js

Add compatibility for 'gas' type of 'device_class'

* Specify that 'co' is default for gas binary sensor

* Clean up formatting before merging

* Minor spelling correction

* Delete README.md

* Revert "Delete README.md"

This reverts commit 6bffc4e.

* Update README.md

* Add alarm_control_panel.js

* Update to allow alarm_control_panel

* Temporary workaround for "NIGHT_ARM"

Due to Home Assistant limitations, arming "Night" is not currently possible (temporarily redirected to "Home").

* Update README.md

* Clean up #1

* Clean up #2

* Clean up #3
  • Loading branch information
schmittx authored and robbiet480 committed Jul 19, 2017
1 parent 40c2ccc commit fd264c6
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 1 deletion.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ automatically. Easy peasey.

Here's a list of the devices that are currently exposed:

* **Alarm Control Panels** - arm (home, away, night), disarm, triggered
* **Binary Sensor** - door, leak, moisture, motion, smoke, and window state
* **Climate** - current temperature, target temperature, heat/cool mode
* **Cover** - exposed as a garage door or window covering (see notes)
Expand All @@ -33,6 +34,10 @@ Here's a list of the devices that are currently exposed:
* **Sensors** - carbon dioxide (CO2), humidity, light, temperature sensors
* **Switches** - on/off

### Alarm Control Panel Support

Home Assistant does not currently support "Night" arming. For now, selecting "Night" within HomeKit apps will set the system to "Home".

### Binary Sensor Support

Binary Sensors must have a `device_class` set. Accepted `device_class`es are `gas`, `moisture`, `motion`, `occupancy`, `opening` and `smoke`.
Expand Down
179 changes: 179 additions & 0 deletions accessories/alarm_control_panel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
'use strict';

let Service;
let Characteristic;
let communicationError;

function HomeAssistantAlarmControlPanel(log, data, client) {
// device info
this.domain = 'alarm_control_panel';
this.data = data;
this.entity_id = data.entity_id;
this.uuid_base = data.entity_id;
if (data.attributes && data.attributes.friendly_name) {
this.name = data.attributes.friendly_name;
} else {
this.name = data.entity_id.split('.').pop().replace(/_/g, ' ');
}
if (data.attributes && data.attributes.homebridge_mfg) {
this.mfg = String(data.attributes.homebridge_mfg);
} else {
this.mfg = 'Home Assistant';
}
if (data.attributes && data.attributes.homebridge_model) {
this.model = String(data.attributes.homebridge_model);
} else {
this.model = 'Alarm Control Panel';
}
if (data.attributes && data.attributes.homebridge_serial) {
this.serial = String(data.attributes.homebridge_serial);
} else {
this.serial = data.entity_id;
}
this.client = client;
this.log = log;
}

HomeAssistantAlarmControlPanel.prototype = {
onEvent(oldState, newState) {
let alarmState;
if (newState.state === 'armed_home') {
alarmState = 0;
} else if (newState.state === 'armed_away') {
alarmState = 1;
} else if (newState.state === 'armed_night') {
alarmState = 2;
} else if (newState.state === 'disarmed') {
alarmState = 3;
} else if (newState.state === 'triggered') {
alarmState = 4;
} else {
alarmState = 3;
}
this.alarmService.getCharacteristic(Characteristic.SecuritySystemCurrentState)
.setValue(alarmState, null, 'internal');
this.alarmService.getCharacteristic(Characteristic.SecuritySystemTargetState)
.setValue(alarmState, null, 'internal');
},
getAlarmState(callback) {
this.client.fetchState(this.entity_id, (data) => {
if (data) {
if (data.state === 'armed_home') {
callback(null, 0);
} else if (data.state === 'armed_away') {
callback(null, 1);
} else if (data.state === 'armed_night') {
callback(null, 2);
} else if (data.state === 'disarmed') {
callback(null, 3);
} else if (data.state === 'triggered') {
callback(null, 4);
} else {
callback(null, 3);
}
} else {
callback(communicationError);
}
});
},

setAlarmState(targetState, callback, context) {
if (context === 'internal') {
callback();
return;
}

const that = this;
const serviceData = {};
serviceData.entity_id = this.entity_id;

if (targetState === Characteristic.SecuritySystemCurrentState.STAY_ARM) {
this.log(`Setting alarm state on the '${this.name}' to armed stay`);

this.client.callService(this.domain, 'alarm_arm_home', serviceData, (data) => {
if (data) {
that.log(`Successfully set alarm state on the '${that.name}' to armed stay`);
callback();
} else {
callback(communicationError);
}
});
} else if (targetState === Characteristic.SecuritySystemCurrentState.AWAY_ARM) {
this.log(`Setting alarm state on the '${this.name}' to armed stay`);

this.client.callService(this.domain, 'alarm_arm_away', serviceData, (data) => {
if (data) {
that.log(`Successfully set alarm state on the '${that.name}' to armed away`);
callback();
} else {
callback(communicationError);
}
});
} else if (targetState === Characteristic.SecuritySystemCurrentState.NIGHT_ARM) {
this.log(`Setting alarm state on the '${this.name}' to armed night`);

this.client.callService(this.domain, 'alarm_arm_home', serviceData, (data) => {
if (data) {
that.log(`Successfully set alarm state on the '${that.name}' to armed night`);
callback();
} else {
callback(communicationError);
}
});
} else if (targetState === Characteristic.SecuritySystemCurrentState.DISARMED) {
this.log(`Setting alarm state on the '${this.name}' to disarmed`);

this.client.callService(this.domain, 'alarm_disarm', serviceData, (data) => {
if (data) {
that.log(`Successfully set alarm state on the '${that.name}' to disarmed`);
callback();
} else {
callback(communicationError);
}
});
} else {
this.log(`Setting alarm state on the '${this.name}' to disarmed`);

this.client.callService(this.domain, 'alarm_disarm', serviceData, (data) => {
if (data) {
that.log(`Successfully set alarm state on the '${that.name}' to disarmed`);
callback();
} else {
callback(communicationError);
}
});
}
},
getServices() {
this.alarmService = new Service.SecuritySystem();
const informationService = new Service.AccessoryInformation();

informationService
.setCharacteristic(Characteristic.Manufacturer, this.mfg)
.setCharacteristic(Characteristic.Model, this.model)
.setCharacteristic(Characteristic.SerialNumber, this.serial);

this.alarmService
.getCharacteristic(Characteristic.SecuritySystemCurrentState)
.on('get', this.getAlarmState.bind(this));

this.alarmService
.getCharacteristic(Characteristic.SecuritySystemTargetState)
.on('get', this.getAlarmState.bind(this))
.on('set', this.setAlarmState.bind(this));

return [informationService, this.alarmService];
},

};

function HomeAssistantAlarmControlPanelPlatform(oService, oCharacteristic, oCommunicationError) {
Service = oService;
Characteristic = oCharacteristic;
communicationError = oCommunicationError;

return HomeAssistantAlarmControlPanel;
}

module.exports = HomeAssistantAlarmControlPanelPlatform;
module.exports.HomeAssistantAlarmControlPanel = HomeAssistantAlarmControlPanel;
6 changes: 5 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const EventSource = require('eventsource');

const communicationError = new Error('Can not communicate with Home Assistant.');

let HomeAssistantAlarmControlPanel;
let HomeAssistantBinarySensorFactory;
let HomeAssistantCoverFactory;
let HomeAssistantFan;
Expand All @@ -23,7 +24,7 @@ function HomeAssistantPlatform(log, config, api) {
// auth info
this.host = config.host;
this.password = config.password;
this.supportedTypes = config.supported_types || ['binary_sensor', 'climate', 'cover', 'device_tracker', 'fan', 'group', 'input_boolean', 'light', 'lock', 'media_player', 'scene', 'sensor', 'switch', 'remote'];
this.supportedTypes = config.supported_types || ['alarm_control_panel', 'binary_sensor', 'climate', 'cover', 'device_tracker', 'fan', 'group', 'input_boolean', 'light', 'lock', 'media_player', 'remote', 'scene', 'sensor', 'switch'];
this.foundAccessories = [];
this.logging = config.logging !== undefined ? config.logging : true;
this.verify_ssl = config.verify_ssl !== undefined ? config.verify_ssl : true;
Expand Down Expand Up @@ -186,6 +187,8 @@ HomeAssistantPlatform.prototype = {
accessory = HomeAssistantBinarySensorFactory(that.log, entity, that);
} else if (entityType === 'group') {
accessory = new HomeAssistantSwitch(that.log, entity, that, 'group');
} else if (entityType === 'alarm_control_panel') {
accessory = new HomeAssistantAlarmControlPanel(that.log, entity, that);
} else if (entityType === 'remote') {
accessory = new HomeAssistantSwitch(that.log, entity, that, 'remote');
}
Expand Down Expand Up @@ -215,6 +218,7 @@ function HomebridgeHomeAssistant(homebridge) {
HomeAssistantBinarySensorFactory = require('./accessories/binary_sensor')(Service, Characteristic, communicationError);
HomeAssistantDeviceTrackerFactory = require('./accessories/device_tracker')(Service, Characteristic, communicationError);
HomeAssistantClimate = require('./accessories/climate')(Service, Characteristic, communicationError);
HomeAssistantAlarmControlPanel = require('./accessories/alarm_control_panel')(Service, Characteristic, communicationError);
/* eslint-enable global-require */

homebridge.registerPlatform('homebridge-homeassistant', 'HomeAssistant', HomeAssistantPlatform, false);
Expand Down

0 comments on commit fd264c6

Please sign in to comment.