mirror of
https://github.com/mtan93/homebridge.git
synced 2026-03-08 21:02:38 +00:00
Merge branch 'nfarina/master'
This commit is contained in:
58
accessories/GenericRS232Device.js
Normal file
58
accessories/GenericRS232Device.js
Normal file
@@ -0,0 +1,58 @@
|
||||
var Service = require("HAP-NodeJS").Service;
|
||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
||||
var SerialPort = require("serialport").SerialPort;
|
||||
|
||||
module.exports = {
|
||||
accessory: GenericRS232DeviceAccessory
|
||||
}
|
||||
|
||||
function GenericRS232DeviceAccessory(log, config) {
|
||||
this.log = log;
|
||||
this.id = config["id"];
|
||||
this.name = config["name"];
|
||||
this.model_name = config["model_name"];
|
||||
this.manufacturer = config["manufacturer"];
|
||||
this.on_command = config["on_command"];
|
||||
this.off_command = config["off_command"];
|
||||
this.device = config["device"];
|
||||
this.baudrate = config["baudrate"];
|
||||
}
|
||||
|
||||
GenericRS232DeviceAccessory.prototype = {
|
||||
setPowerState: function(powerOn, callback) {
|
||||
var that = this;
|
||||
var command = powerOn ? that.on_command : that.off_command;
|
||||
var serialPort = new SerialPort(that.device, { baudrate: that.baudrate }, false);
|
||||
serialPort.open(function (error) {
|
||||
if (error) {
|
||||
callback(new Error('Can not communicate with ' + that.name + " (" + error + ")"))
|
||||
} else {
|
||||
serialPort.write(command, function(err, results) {
|
||||
if (error) {
|
||||
callback(new Error('Can not send power command to ' + that.name + " (" + err + ")"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getServices: function() {
|
||||
var switchService = new Service.Switch(this.name);
|
||||
var informationService = new Service.AccessoryInformation();
|
||||
|
||||
informationService
|
||||
.setCharacteristic(Characteristic.Manufacturer, this.manufacturer)
|
||||
.setCharacteristic(Characteristic.Model, this.model_name)
|
||||
.setCharacteristic(Characteristic.SerialNumber, this.id);
|
||||
|
||||
switchService
|
||||
.getCharacteristic(Characteristic.On)
|
||||
.on('set', this.setPowerState.bind(this));
|
||||
|
||||
return [informationService, switchService];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.accessory = GenericRS232DeviceAccessory;
|
||||
@@ -140,9 +140,7 @@ WeMoAccessory.prototype.getServices = function() {
|
||||
|
||||
garageDoorService
|
||||
.getCharacteristic(Characteristic.TargetDoorState)
|
||||
.on('set', this.setTargetDoorState.bind(this))
|
||||
.supportsEventNotification = false;
|
||||
|
||||
.on('set', this.setTargetDoorState.bind(this));
|
||||
|
||||
return [garageDoorService];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
/*
|
||||
* This is a KNX universal accessory shim.
|
||||
* This is NOT the version for dynamic installation
|
||||
*
|
||||
New 2015-09-16: Welcome iOS9.0
|
||||
new features includ:
|
||||
services:
|
||||
Window
|
||||
WindowCovering
|
||||
ContactSensor
|
||||
*
|
||||
*/
|
||||
var Service = require("HAP-NodeJS").Service;
|
||||
@@ -23,7 +30,7 @@ function KNXDevice(log, config) {
|
||||
if (config.knxd_ip){
|
||||
this.knxd_ip = config.knxd_ip;
|
||||
} else {
|
||||
throw new Error("MISSING KNXD IP");
|
||||
throw new Error("KNX configuration fault: MISSING KNXD IP");
|
||||
}
|
||||
if (config.knxd_port){
|
||||
this.knxd_port = config.knxd_port;
|
||||
@@ -87,7 +94,7 @@ KNXDevice.prototype = {
|
||||
this.log("[ERROR] knxwrite:sendAPDU: " + err);
|
||||
callback(err);
|
||||
} else {
|
||||
// this.log("knx data sent");
|
||||
this.log("knx data sent: Value "+value+ " for GA "+groupAddress);
|
||||
callback();
|
||||
}
|
||||
}.bind(this));
|
||||
@@ -160,7 +167,7 @@ KNXDevice.prototype = {
|
||||
knxregister_bool: function(addresses, characteristic) {
|
||||
this.log("knx registering BOOLEAN " + addresses);
|
||||
knxd_registerGA(addresses, function(val, src, dest, type){
|
||||
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type"+type + " for " + characteristic.displayName);
|
||||
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type + " for " + characteristic.displayName);
|
||||
// iterate(characteristic);
|
||||
characteristic.setValue(val ? 1 : 0, undefined, 'fromKNXBus');
|
||||
}.bind(this));
|
||||
@@ -168,7 +175,7 @@ KNXDevice.prototype = {
|
||||
knxregister_boolReverse: function(addresses, characteristic) {
|
||||
this.log("knx registering BOOLEAN " + addresses);
|
||||
knxd_registerGA(addresses, function(val, src, dest, type){
|
||||
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type"+type + " for " + characteristic.displayName);
|
||||
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type + " for " + characteristic.displayName);
|
||||
// iterate(characteristic);
|
||||
characteristic.setValue(val ? 0 : 1, undefined, 'fromKNXBus');
|
||||
}.bind(this));
|
||||
@@ -177,7 +184,7 @@ KNXDevice.prototype = {
|
||||
knxregister_percent: function(addresses, characteristic) {
|
||||
this.log("knx registering PERCENT " + addresses);
|
||||
knxd_registerGA(addresses, function(val, src, dest, type){
|
||||
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type"+type+ " for " + characteristic.displayName);
|
||||
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName);
|
||||
if (type !== "DPT5") {
|
||||
this.log("[ERROR] Received value cannot be a percentage value");
|
||||
} else {
|
||||
@@ -199,7 +206,7 @@ KNXDevice.prototype = {
|
||||
knxregister_float: function(addresses, characteristic) {
|
||||
this.log("knx registering FLOAT " + addresses);
|
||||
knxd_registerGA(addresses, function(val, src, dest, type){
|
||||
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type"+type+ " for " + characteristic.displayName);
|
||||
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName);
|
||||
var hk_value = Math.round(val*10)/10;
|
||||
if (hk_value>=characteristic.minimumValue && hk_value<=characteristic.maximumValue) {
|
||||
characteristic.setValue(hk_value, undefined, 'fromKNXBus'); // 1 decoimal for HomeKit
|
||||
@@ -214,7 +221,7 @@ KNXDevice.prototype = {
|
||||
knxregister_HVAC: function(addresses, characteristic) {
|
||||
this.log("knx registering HVAC " + addresses);
|
||||
knxd_registerGA(addresses, function(val, src, dest, type){
|
||||
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type"+type+ " for " + characteristic.displayName);
|
||||
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName);
|
||||
var HAPvalue = 0;
|
||||
switch (val){
|
||||
case 0:
|
||||
@@ -256,7 +263,7 @@ KNXDevice.prototype = {
|
||||
knxregister: function(addresses, characteristic) {
|
||||
this.log("knx registering " + addresses);
|
||||
knxd_registerGA(addresses, function(val, src, dest, type){
|
||||
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type"+type+ " for " + characteristic.displayName);
|
||||
this.log("Received value from bus:"+val+ " for " +dest+ " from "+src+" of type "+type+ " for " + characteristic.displayName);
|
||||
characteristic.setValue(val, undefined, 'fromKNXBus');
|
||||
}.bind(this));
|
||||
},
|
||||
@@ -552,16 +559,26 @@ KNXDevice.prototype = {
|
||||
// TargetTemperature if available
|
||||
if (config.TargetTemperature) {
|
||||
this.log("Thermostat TargetTemperature characteristic enabled");
|
||||
|
||||
// DEBUG
|
||||
console.log("default value: " + myService.getCharacteristic(Characteristic.TargetTemperature).value);
|
||||
// DEBUG
|
||||
|
||||
// default boundary too narrow for thermostats
|
||||
myService.getCharacteristic(Characteristic.TargetTemperature).minimumValue=0; // °C
|
||||
myService.getCharacteristic(Characteristic.TargetTemperature).maximumValue=40; // °C
|
||||
this.bindCharacteristic(myService, Characteristic.TargetTemperature, "Float", config.TargetTemperature);
|
||||
}
|
||||
// HVAC missing yet
|
||||
// HVAC
|
||||
if (config.CurrentHeatingCoolingState) {
|
||||
this.log("Thermostat CurrentHeatingCoolingState characteristic enabled");
|
||||
this.bindCharacteristic(myService, Characteristic.CurrentHeatingCoolingState, "HVAC", config.CurrentHeatingCoolingState);
|
||||
}
|
||||
// HVAC
|
||||
if (config.TargetHeatingCoolingState) {
|
||||
this.log("Thermostat TargetHeatingCoolingState characteristic enabled");
|
||||
this.bindCharacteristic(myService, Characteristic.TargetHeatingCoolingState, "HVAC", config.TargetHeatingCoolingState);
|
||||
}
|
||||
return myService;
|
||||
},
|
||||
|
||||
@@ -584,13 +601,174 @@ KNXDevice.prototype = {
|
||||
var myService = new Service.TemperatureSensor(config.name,config.name);
|
||||
// CurrentTemperature)
|
||||
if (config.CurrentTemperature) {
|
||||
this.log("Thermostat CurrentTemperature characteristic enabled");
|
||||
this.log("TemperatureSensor CurrentTemperature characteristic enabled");
|
||||
this.bindCharacteristic(myService, Characteristic.CurrentTemperature, "Float", config.CurrentTemperature);
|
||||
}
|
||||
return myService;
|
||||
},
|
||||
|
||||
|
||||
|
||||
// window type (iOS9 assumed)
|
||||
getWindowService: function(config) {
|
||||
// Service.Window = function(displayName, subtype) {
|
||||
// Service.call(this, displayName, '0000008B-0000-1000-8000-0026BB765291', subtype);
|
||||
//
|
||||
// // Required Characteristics
|
||||
// this.addCharacteristic(Characteristic.CurrentPosition);
|
||||
// this.addCharacteristic(Characteristic.TargetPosition);
|
||||
// this.addCharacteristic(Characteristic.PositionState);
|
||||
//
|
||||
// // Optional Characteristics
|
||||
// this.addOptionalCharacteristic(Characteristic.HoldPosition);
|
||||
// this.addOptionalCharacteristic(Characteristic.ObstructionDetected);
|
||||
// this.addOptionalCharacteristic(Characteristic.Name);
|
||||
|
||||
// Characteristic.PositionState.DECREASING = 0;
|
||||
// Characteristic.PositionState.INCREASING = 1;
|
||||
// Characteristic.PositionState.STOPPED = 2;
|
||||
|
||||
|
||||
// some sanity checks
|
||||
|
||||
|
||||
if (config.type !== "Window") {
|
||||
this.log("[ERROR] Window Service for non 'Window' service called");
|
||||
return undefined;
|
||||
}
|
||||
if (!config.name) {
|
||||
this.log("[ERROR] Window Service without 'name' property called");
|
||||
return undefined;
|
||||
}
|
||||
var myService = new Service.Window(config.name,config.name);
|
||||
|
||||
if (config.CurrentPosition) {
|
||||
this.log("Window CurrentPosition characteristic enabled");
|
||||
this.bindCharacteristic(myService, Characteristic.CurrentPosition, "Percent", config.CurrentPosition);
|
||||
}
|
||||
if (config.TargetPosition) {
|
||||
this.log("Window TargetPosition characteristic enabled");
|
||||
this.bindCharacteristic(myService, Characteristic.TargetPosition, "Percent", config.TargetPosition);
|
||||
}
|
||||
if (config.PositionState) {
|
||||
this.log("Window PositionState characteristic enabled");
|
||||
this.bindCharacteristic(myService, Characteristic.PositionState, "Float", config.PositionState);
|
||||
}
|
||||
return myService;
|
||||
},
|
||||
|
||||
|
||||
// /**
|
||||
// * Service "Window Covering"
|
||||
// */
|
||||
//
|
||||
// Service.WindowCovering = function(displayName, subtype) {
|
||||
// Service.call(this, displayName, '0000008C-0000-1000-8000-0026BB765291', subtype);
|
||||
//
|
||||
// // Required Characteristics
|
||||
// this.addCharacteristic(Characteristic.CurrentPosition);
|
||||
// this.addCharacteristic(Characteristic.TargetPosition);
|
||||
// this.addCharacteristic(Characteristic.PositionState);
|
||||
//
|
||||
// // Optional Characteristics
|
||||
// this.addOptionalCharacteristic(Characteristic.HoldPosition);
|
||||
// this.addOptionalCharacteristic(Characteristic.TargetHorizontalTiltAngle);
|
||||
// this.addOptionalCharacteristic(Characteristic.TargetVerticalTiltAngle);
|
||||
// this.addOptionalCharacteristic(Characteristic.CurrentHorizontalTiltAngle);
|
||||
// this.addOptionalCharacteristic(Characteristic.CurrentVerticalTiltAngle);
|
||||
// this.addOptionalCharacteristic(Characteristic.ObstructionDetected);
|
||||
// this.addOptionalCharacteristic(Characteristic.Name);
|
||||
// };
|
||||
getWindowCoveringService: function(config) {
|
||||
|
||||
// some sanity checks
|
||||
|
||||
|
||||
if (config.type !== "WindowCovering") {
|
||||
this.log("[ERROR] WindowCovering Service for non 'WindowCovering' service called");
|
||||
return undefined;
|
||||
}
|
||||
if (!config.name) {
|
||||
this.log("[ERROR] WindowCovering Service without 'name' property called");
|
||||
return undefined;
|
||||
}
|
||||
var myService = new Service.WindowCovering(config.name,config.name);
|
||||
|
||||
if (config.CurrentPosition) {
|
||||
this.log("WindowCovering CurrentPosition characteristic enabled");
|
||||
this.bindCharacteristic(myService, Characteristic.CurrentPosition, "Percent", config.CurrentPosition);
|
||||
}
|
||||
if (config.TargetPosition) {
|
||||
this.log("WindowCovering TargetPosition characteristic enabled");
|
||||
this.bindCharacteristic(myService, Characteristic.TargetPosition, "Percent", config.TargetPosition);
|
||||
}
|
||||
if (config.PositionState) {
|
||||
this.log("WindowCovering PositionState characteristic enabled");
|
||||
this.bindCharacteristic(myService, Characteristic.PositionState, "Float", config.PositionState);
|
||||
}
|
||||
return myService;
|
||||
},
|
||||
|
||||
// Service.ContactSensor = function(displayName, subtype) {
|
||||
// Service.call(this, displayName, '00000080-0000-1000-8000-0026BB765291', subtype);
|
||||
//
|
||||
// // Required Characteristics
|
||||
// this.addCharacteristic(Characteristic.ContactSensorState);
|
||||
//
|
||||
// // Optional Characteristics
|
||||
// this.addOptionalCharacteristic(Characteristic.StatusActive);
|
||||
// this.addOptionalCharacteristic(Characteristic.StatusFault);
|
||||
// this.addOptionalCharacteristic(Characteristic.StatusTampered);
|
||||
// this.addOptionalCharacteristic(Characteristic.StatusLowBattery);
|
||||
// this.addOptionalCharacteristic(Characteristic.Name);
|
||||
// };
|
||||
// Characteristic.ContactSensorState.CONTACT_DETECTED = 0;
|
||||
// Characteristic.ContactSensorState.CONTACT_NOT_DETECTED = 1;
|
||||
getContactSenserService: function(config) {
|
||||
// some sanity checks
|
||||
if (config.type !== "ContactSensor") {
|
||||
this.log("[ERROR] ContactSensor Service for non 'ContactSensor' service called");
|
||||
return undefined;
|
||||
}
|
||||
if (!config.name) {
|
||||
this.log("[ERROR] ContactSensor Service without 'name' property called");
|
||||
return undefined;
|
||||
}
|
||||
var myService = new Service.ContactSensor(config.name,config.name);
|
||||
|
||||
if (config.ContactSensorState) {
|
||||
this.log("ContactSensor ContactSensorState characteristic enabled");
|
||||
this.bindCharacteristic(myService, Characteristic.ContactSensorState, "Bool", config.ContactSensorState);
|
||||
} else if (config.ContactSensorStateContact1) {
|
||||
this.log("ContactSensor ContactSensorStateContact1 characteristic enabled");
|
||||
this.bindCharacteristic(myService, Characteristic.ContactSensorState, "BoolReverse", config.ContactSensorStateContact1);
|
||||
}
|
||||
//optionals
|
||||
if (config.StatusActive) {
|
||||
this.log("ContactSensor StatusActive characteristic enabled");
|
||||
myService.addCharacteristic(Characteristic.StatusActive);
|
||||
this.bindCharacteristic(myService, Characteristic.StatusActive, "Bool", config.StatusActive);
|
||||
}
|
||||
if (config.StatusFault) {
|
||||
this.log("ContactSensor StatusFault characteristic enabled");
|
||||
myService.addCharacteristic(Characteristic.StatusFault);
|
||||
this.bindCharacteristic(myService, Characteristic.StatusFault, "Bool", config.StatusFault);
|
||||
}
|
||||
if (config.StatusTampered) {
|
||||
this.log("ContactSensor StatusTampered characteristic enabled");
|
||||
myService.addCharacteristic(Characteristic.StatusTampered);
|
||||
this.bindCharacteristic(myService, Characteristic.StatusTampered, "Bool", config.StatusTampered);
|
||||
}
|
||||
if (config.StatusLowBattery) {
|
||||
this.log("ContactSensor StatusLowBattery characteristic enabled");
|
||||
myService.addCharacteristic(Characteristic.StatusLowBattery);
|
||||
this.bindCharacteristic(myService, Characteristic.StatusLowBattery, "Bool", config.StatusLowBattery);
|
||||
}
|
||||
return myService;
|
||||
},
|
||||
|
||||
|
||||
|
||||
/* assemble the device ***************************************************************************************************/
|
||||
|
||||
|
||||
@@ -625,7 +803,11 @@ KNXDevice.prototype = {
|
||||
this.log("[ERROR] must specify 'type' and 'name' properties for each service in config.json. KNX platform section fault ");
|
||||
throw new Error("Must specify 'type' and 'name' properties for each service in config.json");
|
||||
}
|
||||
this.log("Preparing Service: " + int + " of type "+configService.type)
|
||||
switch (configService.type) {
|
||||
case "ContactSensor":
|
||||
accessoryServices.push(this.getContactSenserService(configService));
|
||||
break;
|
||||
case "Lightbulb":
|
||||
accessoryServices.push(this.getLightbulbService(configService));
|
||||
break;
|
||||
@@ -638,8 +820,14 @@ KNXDevice.prototype = {
|
||||
case "Thermostat":
|
||||
accessoryServices.push(this.getThermostatService(configService));
|
||||
break;
|
||||
case "Window":
|
||||
accessoryServices.push(this.getWindowService(configService));
|
||||
break;
|
||||
case "WindowCovering":
|
||||
accessoryServices.push(this.getWindowCoveringService(configService));
|
||||
break;
|
||||
default:
|
||||
this.log("[ERROR] unknown 'type' property for service "+ configService.name + " in config.json. KNX platform section fault ");
|
||||
this.log("[ERROR] unknown 'type' property of '"+configService.type+"' for service "+ configService.name + " in config.json. KNX platform section fault ");
|
||||
//throw new Error("[ERROR] unknown 'type' property for service "+ configService.name + " in config.json. KNX platform section fault ");
|
||||
}
|
||||
}
|
||||
|
||||
126
accessories/knxdevice.md
Normal file
126
accessories/knxdevice.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Syntax of the config.json
|
||||
In the platforms section, you can insert a KNX type platform.
|
||||
You need to configure all devices directly in the config.json.
|
||||
|
||||
"platforms": [
|
||||
{
|
||||
"platform": "KNX",
|
||||
"name": "KNX",
|
||||
"knxd_ip": "192.168.178.205",
|
||||
"knxd_port": 6720,
|
||||
"accessories": [
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"name": "Living Room North Lamp",
|
||||
"services": [
|
||||
{
|
||||
"type": "Lightbulb",
|
||||
"description": "iOS8 Lightbulb type, supports On (Switch) and Brightness",
|
||||
"name": "Living Room North Lamp",
|
||||
"On": {
|
||||
"Set": "1/1/6",
|
||||
"Listen": [
|
||||
"1/1/63"
|
||||
]
|
||||
},
|
||||
"Brightness": {
|
||||
"Set": "1/1/62",
|
||||
"Listen": [
|
||||
"1/1/64"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
In the accessories section (the array within the brackets [ ]) you can insert as many objects as you like in the following form
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"name": "Here goes your display name, this will be shown in HomeKit apps",
|
||||
"services": [
|
||||
{
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
You have to add services in the following syntax:
|
||||
{
|
||||
"type": "SERVICENAME",
|
||||
"description": "This is just for you to remember things",
|
||||
"name": "We need a name for each service, though it usually shows only if multiple services are present in one accessory",
|
||||
"CHARACTERISTIC1": {
|
||||
"Set": "1/1/6",
|
||||
"Listen": [
|
||||
"1/1/63"
|
||||
]
|
||||
},
|
||||
"CHARACTERISTIC2": {
|
||||
"Set": "1/1/62",
|
||||
"Listen": [
|
||||
"1/1/64"
|
||||
]
|
||||
}
|
||||
}
|
||||
CHARACTERISTIC are properties that are dependent on the service type, so they are listed below.
|
||||
Two kinds of addresses are supported: "Set":"1/2/3" is a writable group address, to which changes are sent if the service supports changing values. Changes on the bus are listened to, too.
|
||||
"Listen":["1/2/3","1/2/4","1/2/5"] is an array of addresses that are listened to additionally. To these addresses never values get written, but the on startup the service will issue read requests to ALL addresses listed in Set: and in Listen:
|
||||
|
||||
|
||||
# Supported Services and their characteristics
|
||||
|
||||
## Lightbulb
|
||||
On: DPT 1, 1 as on, 0 as off
|
||||
Brightness: DPT5 percentage, 100% (=255) the brightest
|
||||
|
||||
|
||||
## LockMechanism
|
||||
LockCurrentState: DPT 1, 1 as secured
|
||||
OR (but not both:)
|
||||
LockCurrentStateSecured0: DPT 1, 0 as secured
|
||||
|
||||
LockTargetState: DPT 1, 1 as secured
|
||||
LockTargetStateSecured0: DPT 1, 0 as secured
|
||||
|
||||
## Thermostat
|
||||
CurrentTemperature: DPT9 in °C [listen only]
|
||||
TargetTemperature: DPT9, values 0..40°C only, all others are ignored
|
||||
CurrentHeatingCoolingState: DPT5 HVAC, because of the incompatible mapping only off and heating (=auto) are shown, [listen only]
|
||||
TargetHeatingCoolingState: as above
|
||||
|
||||
|
||||
## TemperatureSensor
|
||||
CurrentTemperature: DPT9 in °C [listen only]
|
||||
|
||||
## Window
|
||||
CurrentPosition: DPT5 percentage
|
||||
TargetPosition: DPT5 percentage
|
||||
PositionState: DPT5 value [listen only]
|
||||
|
||||
## WindowCovering
|
||||
CurrentPosition: DPT5 percentage
|
||||
TargetPosition: DPT5 percentage
|
||||
PositionState: DPT5 value [listen only]
|
||||
|
||||
### not yet supported
|
||||
HoldPosition
|
||||
TargetHorizontalTiltAngle
|
||||
TargetVerticalTiltAngle
|
||||
CurrentHorizontalTiltAngle
|
||||
CurrentVerticalTiltAngle
|
||||
ObstructionDetected
|
||||
|
||||
## ContactSensor
|
||||
ContactSensorState: DPT 1, 0 as contact
|
||||
OR
|
||||
ContactSensorStateContact1: DPT 1, 1 as contact
|
||||
|
||||
StatusActive: DPT 1, 1 as true
|
||||
StatusFault: DPT 1, 1 as true
|
||||
StatusTampered: DPT 1, 1 as true
|
||||
StatusLowBattery: DPT 1, 1 as true
|
||||
|
||||
|
||||
# DISCLAIMER
|
||||
This is work in progress!
|
||||
|
||||
11
app.js
11
app.js
@@ -191,6 +191,16 @@ function createAccessory(accessoryInstance, displayName) {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the setup code in a scannable format.
|
||||
function printPin(pin) {
|
||||
console.log("Scan this code with your HomeKit App on your iOS device:");
|
||||
console.log("\x1b[30;47m%s\x1b[0m", " ");
|
||||
console.log("\x1b[30;47m%s\x1b[0m", " ┌────────────┐ ");
|
||||
console.log("\x1b[30;47m%s\x1b[0m", " │ " + pin + " │ ");
|
||||
console.log("\x1b[30;47m%s\x1b[0m", " └────────────┘ ");
|
||||
console.log("\x1b[30;47m%s\x1b[0m", " ");
|
||||
}
|
||||
|
||||
// Returns a logging function that prepends messages with the given name in [brackets].
|
||||
function createLog(name) {
|
||||
return function(message) {
|
||||
@@ -201,6 +211,7 @@ function createLog(name) {
|
||||
}
|
||||
|
||||
function publish() {
|
||||
printPin(bridgeConfig.pin);
|
||||
bridge.publish({
|
||||
username: bridgeConfig.username || "CC:22:3D:E3:CE:30",
|
||||
port: bridgeConfig.port || 51826,
|
||||
|
||||
@@ -94,7 +94,8 @@
|
||||
"platform": "HomeAssistant",
|
||||
"name": "HomeAssistant",
|
||||
"host": "http://192.168.1.10:8123",
|
||||
"password": "XXXXX"
|
||||
"password": "XXXXX",
|
||||
"supported_types": ["light", "switch", "media_player", "scene"]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -204,7 +205,18 @@
|
||||
"window_seconds": 5,
|
||||
"sensor_type": "m",
|
||||
"inverse": false
|
||||
},
|
||||
{
|
||||
"accessory": "GenericRS232Device",
|
||||
"name": "Projector",
|
||||
"description": "Make sure you set a 'Siri-Name' for your iOS-Device (example: 'Home Cinema') otherwise it might not work.",
|
||||
"id": "TYDYMU044UVNP",
|
||||
"baudrate": 9600,
|
||||
"device": "/dev/tty.usbserial",
|
||||
"manufacturer": "Acer",
|
||||
"model_name": "H6510BD",
|
||||
"on_command": "* 0 IR 001\r",
|
||||
"off_command": "* 0 IR 002\r"
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"color": "0.10.x",
|
||||
"eibd": "^0.3.1",
|
||||
"elkington": "kevinohara80/elkington",
|
||||
"hap-nodejs": "git+https://github.com/KhaosT/HAP-NodeJS#98ef550c8d6fd961741673d4b695a74dd0126eba",
|
||||
"hap-nodejs": "git+https://github.com/KhaosT/HAP-NodeJS#0030b35856e04ee2b42f0d05839feaa5c44cbd1f",
|
||||
"harmonyhubjs-client": "^1.1.4",
|
||||
"harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git",
|
||||
"lifx-api": "^1.0.1",
|
||||
|
||||
@@ -7,7 +7,27 @@
|
||||
// URL: http://home-assistant.io
|
||||
// GitHub: https://github.com/balloob/home-assistant
|
||||
//
|
||||
// HA accessories supported: Lights, Switches, Media Players.
|
||||
// HA accessories supported: Lights, Switches, Media Players, Scenes.
|
||||
//
|
||||
// Optional Devices - Edit the supported_types key in the config to pick which
|
||||
// of the 4 types you would like to expose to HomeKit from
|
||||
// Home Assistant. light, switch, media_player, scene.
|
||||
//
|
||||
//
|
||||
// Scene Support
|
||||
//
|
||||
// You can optionally import your Home Assistant scenes. These will appear to
|
||||
// HomeKit as switches. You can simply say "turn on party time". In some cases
|
||||
// scenes names are already rerved in HomeKit...like "Good Morning" and
|
||||
// "Good Night". You will be able to just say "Good Morning" or "Good Night" to
|
||||
// have these triggered.
|
||||
//
|
||||
// You might want to play with the wording to figure out what ends up working well
|
||||
// for your scene names. It's also important to not populate any actual HomeKit
|
||||
// scenes with the same names, as Siri will pick these instead of your Home
|
||||
// Assistant scenes.
|
||||
//
|
||||
//
|
||||
//
|
||||
// Media Player Support
|
||||
//
|
||||
@@ -25,6 +45,8 @@
|
||||
// will need to use the same language you use to set the brighness of a light.
|
||||
// You can play around with language to see what fits best.
|
||||
//
|
||||
//
|
||||
//
|
||||
// Examples
|
||||
//
|
||||
// Dim the Kitchen Speaker to 40% - sets volume to 40%
|
||||
@@ -37,7 +59,8 @@
|
||||
// "platform": "HomeAssistant",
|
||||
// "name": "HomeAssistant",
|
||||
// "host": "http://192.168.1.50:8123",
|
||||
// "password": "xxx"
|
||||
// "password": "xxx",
|
||||
// "supported_types": ["light", "switch", "media_player", "scene"]
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
@@ -56,6 +79,7 @@ function HomeAssistantPlatform(log, config){
|
||||
// auth info
|
||||
this.host = config["host"];
|
||||
this.password = config["password"];
|
||||
this.supportedTypes = config["supported_types"];
|
||||
|
||||
this.log = log;
|
||||
}
|
||||
@@ -121,22 +145,26 @@ HomeAssistantPlatform.prototype = {
|
||||
|
||||
var that = this;
|
||||
var foundAccessories = [];
|
||||
var lightsRE = /^light\./i
|
||||
var switchRE = /^switch\./i
|
||||
var mediaPlayerRE = /^media_player\./i
|
||||
|
||||
|
||||
this._request('GET', '/states', {}, function(error, response, data){
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
entity = data[i]
|
||||
entity_type = entity.entity_id.split('.')[0]
|
||||
|
||||
if (that.supportedTypes.indexOf(entity_type) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var accessory = null
|
||||
|
||||
if (entity.entity_id.match(lightsRE)) {
|
||||
if (entity_type == 'light') {
|
||||
accessory = new HomeAssistantLight(that.log, entity, that)
|
||||
}else if (entity.entity_id.match(switchRE)){
|
||||
}else if (entity_type == 'switch'){
|
||||
accessory = new HomeAssistantSwitch(that.log, entity, that)
|
||||
}else if (entity.entity_id.match(mediaPlayerRE) && entity.attributes && entity.attributes.supported_media_commands){
|
||||
}else if (entity_type == 'scene'){
|
||||
accessory = new HomeAssistantSwitch(that.log, entity, that, 'scene')
|
||||
}else if (entity_type == 'media_player' && entity.attributes && entity.attributes.supported_media_commands){
|
||||
accessory = new HomeAssistantMediaPlayer(that.log, entity, that)
|
||||
}
|
||||
|
||||
@@ -240,6 +268,12 @@ HomeAssistantLight.prototype = {
|
||||
},
|
||||
getServices: function() {
|
||||
var lightbulbService = new Service.Lightbulb();
|
||||
var informationService = new Service.AccessoryInformation();
|
||||
|
||||
informationService
|
||||
.setCharacteristic(Characteristic.Manufacturer, "Home Assistant")
|
||||
.setCharacteristic(Characteristic.Model, "Light")
|
||||
.setCharacteristic(Characteristic.SerialNumber, "xxx");
|
||||
|
||||
lightbulbService
|
||||
.getCharacteristic(Characteristic.On)
|
||||
@@ -251,7 +285,7 @@ HomeAssistantLight.prototype = {
|
||||
.on('get', this.getBrightness.bind(this))
|
||||
.on('set', this.setBrightness.bind(this));
|
||||
|
||||
return [lightbulbService];
|
||||
return [informationService, lightbulbService];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -375,6 +409,12 @@ HomeAssistantMediaPlayer.prototype = {
|
||||
},
|
||||
getServices: function() {
|
||||
var lightbulbService = new Service.Lightbulb();
|
||||
var informationService = new Service.AccessoryInformation();
|
||||
|
||||
informationService
|
||||
.setCharacteristic(Characteristic.Manufacturer, "Home Assistant")
|
||||
.setCharacteristic(Characteristic.Model, "Media Player")
|
||||
.setCharacteristic(Characteristic.SerialNumber, "xxx");
|
||||
|
||||
lightbulbService
|
||||
.getCharacteristic(Characteristic.On)
|
||||
@@ -389,15 +429,15 @@ HomeAssistantMediaPlayer.prototype = {
|
||||
.on('set', this.setVolume.bind(this));
|
||||
}
|
||||
|
||||
return [lightbulbService];
|
||||
return [informationService, lightbulbService];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function HomeAssistantSwitch(log, data, client) {
|
||||
function HomeAssistantSwitch(log, data, client, type) {
|
||||
// device info
|
||||
this.domain = "switch"
|
||||
this.domain = type || "switch"
|
||||
this.data = data
|
||||
this.entity_id = data.entity_id
|
||||
if (data.attributes && data.attributes.friendly_name) {
|
||||
@@ -454,13 +494,35 @@ HomeAssistantSwitch.prototype = {
|
||||
},
|
||||
getServices: function() {
|
||||
var switchService = new Service.Switch();
|
||||
var informationService = new Service.AccessoryInformation();
|
||||
var model;
|
||||
|
||||
switchService
|
||||
.getCharacteristic(Characteristic.On)
|
||||
.on('get', this.getPowerState.bind(this))
|
||||
.on('set', this.setPowerState.bind(this));
|
||||
switch (this.domain) {
|
||||
case "scene":
|
||||
model = "Scene"
|
||||
break;
|
||||
default:
|
||||
model = "Switch"
|
||||
}
|
||||
|
||||
return [switchService];
|
||||
informationService
|
||||
.setCharacteristic(Characteristic.Manufacturer, "Home Assistant")
|
||||
.setCharacteristic(Characteristic.Model, model)
|
||||
.setCharacteristic(Characteristic.SerialNumber, "xxx");
|
||||
|
||||
if (this.domain == 'switch') {
|
||||
switchService
|
||||
.getCharacteristic(Characteristic.On)
|
||||
.on('get', this.getPowerState.bind(this))
|
||||
.on('set', this.setPowerState.bind(this));
|
||||
|
||||
}else{
|
||||
switchService
|
||||
.getCharacteristic(Characteristic.On)
|
||||
.on('set', this.setPowerState.bind(this));
|
||||
}
|
||||
|
||||
return [informationService, switchService];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
},
|
||||
"description": "This is an example configuration file for KNX platform shim",
|
||||
"hint": "Always paste into jsonlint.com validation page before starting your homebridge, saves a lot of frustration",
|
||||
"hint2":"Replace all group addresses by current addresses of your installation, these are arbitrary examples!",
|
||||
"hint3":"For valid services and their characteristics have a look at the knxdevice.md file in folder accessories!"
|
||||
"platforms": [
|
||||
{
|
||||
"platform": "KNX",
|
||||
@@ -16,7 +18,7 @@
|
||||
"accessories": [
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"description": "Only generic type knxdevice is supported, all previous knx type have been merged into that.",
|
||||
"description": "Only generic type knxdevice is supported, all previous knx types have been merged into that.",
|
||||
"name": "Living Room North Lamp",
|
||||
"services": [
|
||||
{
|
||||
@@ -101,19 +103,32 @@
|
||||
"description": "iOS9 Window covering (blinds etc) type, still WIP",
|
||||
"name": "Blinds",
|
||||
"Target": {
|
||||
"Set": "address",
|
||||
"Listen": "adresses"
|
||||
"Set": "1/2/3",
|
||||
"Listen": "1/2/4"
|
||||
},
|
||||
"Current": {
|
||||
"Set": "address",
|
||||
"Listen": "adresses"
|
||||
"Set": "1/3/1",
|
||||
"Listen": "1/3/2"
|
||||
},
|
||||
"PositionState": {
|
||||
"Listen": "adresses"
|
||||
"Listen": "2/7/1"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},{
|
||||
"accessory_type": "knxdevice",
|
||||
|
||||
"description":"sample contact sensor device",
|
||||
"name": "Office",
|
||||
"services": [
|
||||
{
|
||||
"type": "ContactSensor",
|
||||
"name": "Office Door",
|
||||
"ContactSensorState": {
|
||||
"Listen": "5/3/5"
|
||||
}
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
],
|
||||
Reference in New Issue
Block a user