mirror of
https://github.com/mtan93/homebridge.git
synced 2026-04-02 22:04:20 +01:00
Merge branch 'nfarina/master' into zway-more-tags
This commit is contained in:
@@ -152,15 +152,24 @@ HomeAssistantPlatform.prototype = {
|
||||
entity = data[i]
|
||||
entity_type = entity.entity_id.split('.')[0]
|
||||
|
||||
// ignore devices that are not in the list of supported types
|
||||
if (that.supportedTypes.indexOf(entity_type) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ignore hidden devices
|
||||
if (entity.attributes && entity.attributes.hidden) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var accessory = null
|
||||
|
||||
if (entity_type == 'light') {
|
||||
accessory = new HomeAssistantLight(that.log, entity, that)
|
||||
}else if (entity_type == 'switch'){
|
||||
console.log(JSON.stringify(entity))
|
||||
console.log("");
|
||||
console.log("");
|
||||
accessory = new HomeAssistantSwitch(that.log, entity, that)
|
||||
}else if (entity_type == 'scene'){
|
||||
accessory = new HomeAssistantSwitch(that.log, entity, that, 'scene')
|
||||
|
||||
370
platforms/HomeSeer.js
Normal file
370
platforms/HomeSeer.js
Normal file
@@ -0,0 +1,370 @@
|
||||
'use strict';
|
||||
|
||||
//
|
||||
// HomeSeer Platform Shim for HomeBridge
|
||||
// V0.1 - Jean-Michel Joudrier (stipus at stipus dot com) - 2015/10/07 - Initial version
|
||||
//
|
||||
//
|
||||
// Remember to add platform to config.json.
|
||||
//
|
||||
// You can get HomeSeer Device References by clicking a HomeSeer device name, then
|
||||
// choosing the Advanced Tab.
|
||||
//
|
||||
// Example:
|
||||
// "platforms": [
|
||||
// {
|
||||
// "platform": "HomeSeer", // required
|
||||
// "name": "HomeSeer", // required
|
||||
// "url": "http://192.168.3.4:81", // required
|
||||
// "accessories":[
|
||||
// {
|
||||
// "ref":8, // required - HomeSeer Device Reference (To get it, select the HS Device - then Advanced Tab)
|
||||
// "type":"Lightbulb", // Optional - Lightbulb is the default
|
||||
// "name":"My Light", // Optional - HomeSeer device name is the default
|
||||
// "offValue":"0", // Optional - 0 is the default
|
||||
// "onValue":"100", // Optional - 100 is the default
|
||||
// "can_dim":true // Optional - true is the default - false for a non dimmable lightbulb
|
||||
// },
|
||||
// {
|
||||
// "ref":9 // This is a dimmable Lightbulb by default
|
||||
// },
|
||||
// {
|
||||
// "ref":58, // This is an controllable outlet
|
||||
// "type":"Outlet"
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ],
|
||||
//
|
||||
//
|
||||
// SUPORTED TYPES:
|
||||
// - Lightbulb (can_dim, onValue, offValue options)
|
||||
// - Fan (onValue, offValue options)
|
||||
// - Switch (onValue, offValue options)
|
||||
// - Outlet (onValue, offValue options)
|
||||
// - TemperatureSensor
|
||||
// - ContactSensor
|
||||
// - MotionSensor
|
||||
// - LeakSensor
|
||||
// - LightSensor
|
||||
// - OccupancySensor
|
||||
// - SmokeSensor
|
||||
// - Door
|
||||
|
||||
|
||||
var Service = require("HAP-NodeJS").Service;
|
||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
||||
var request = require("request");
|
||||
|
||||
|
||||
function httpRequest(url, method, callback) {
|
||||
request({
|
||||
url: url,
|
||||
method: method
|
||||
},
|
||||
function (error, response, body) {
|
||||
callback(error, response, body)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
function HomeSeerPlatform(log, config){
|
||||
this.log = log;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
HomeSeerPlatform.prototype = {
|
||||
accessories: function(callback) {
|
||||
this.log("Fetching HomeSeer devices.");
|
||||
|
||||
var refList = "";
|
||||
for( var i=0; i<this.config.accessories.length; i++ ) {
|
||||
refList = refList + this.config.accessories[i].ref;
|
||||
if( i < this.config.accessories.length - 1 )
|
||||
refList = refList + ",";
|
||||
}
|
||||
|
||||
var that = this;
|
||||
var foundAccessories = [];
|
||||
var url = this.config["host"] + "/JSON?request=getstatus&ref=" + refList;
|
||||
httpRequest( url, "GET", function(error, response, body) {
|
||||
if (error) {
|
||||
this.log('HomeSeer status function failed: %s', error.message);
|
||||
callback( foundAccessories );
|
||||
}
|
||||
else {
|
||||
this.log('HomeSeer status function succeeded!');
|
||||
var response = JSON.parse( body );
|
||||
for( var i=0; i<response.Devices.length; i++ ) {
|
||||
var accessory = new HomeSeerAccessory( that.log, that.config, response.Devices[i] );
|
||||
foundAccessories.push( accessory );
|
||||
}
|
||||
callback( foundAccessories );
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
function HomeSeerAccessory(log, platformConfig, status ) {
|
||||
this.log = log;
|
||||
this.ref = status.ref;
|
||||
this.name = status.name
|
||||
this.model = status.device_type_string;
|
||||
this.onValue = "100";
|
||||
this.offValue = "0";
|
||||
|
||||
this.control_url = platformConfig["host"] + "/JSON?request=controldevicebyvalue&ref=" + this.ref + "&value=";
|
||||
this.status_url = platformConfig["host"] + "/JSON?request=getstatus&ref=" + this.ref;
|
||||
|
||||
for( var i=0; i<platformConfig.accessories.length; i++ ) {
|
||||
if( platformConfig.accessories[i].ref == this.ref )
|
||||
{
|
||||
this.config = platformConfig.accessories[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( this.config.name )
|
||||
this.name = this.config.name;
|
||||
|
||||
if( this.config.onValue )
|
||||
this.onValue = this.config.onValue;
|
||||
|
||||
if( this.config.offValue )
|
||||
this.offValue = this.config.offValue;
|
||||
}
|
||||
|
||||
HomeSeerAccessory.prototype = {
|
||||
|
||||
identify: function(callback) {
|
||||
callback();
|
||||
},
|
||||
|
||||
setPowerState: function(powerOn, callback) {
|
||||
var url;
|
||||
|
||||
if (powerOn) {
|
||||
url = this.control_url + this.onValue;
|
||||
this.log("Setting power state to on");
|
||||
}
|
||||
else {
|
||||
url = this.control_url + this.offValue;
|
||||
this.log("Setting power state to off");
|
||||
}
|
||||
|
||||
httpRequest(url, 'GET', function(error, response, body) {
|
||||
if (error) {
|
||||
this.log('HomeSeer power function failed: %s', error.message);
|
||||
callback(error);
|
||||
}
|
||||
else {
|
||||
this.log('HomeSeer power function succeeded!');
|
||||
callback();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
getPowerState: function(callback) {
|
||||
var url = this.status_url;
|
||||
|
||||
httpRequest(url, 'GET', function(error, response, body) {
|
||||
if (error) {
|
||||
this.log('HomeSeer get power function failed: %s', error.message);
|
||||
callback( error, 0 );
|
||||
}
|
||||
else {
|
||||
var status = JSON.parse( body );
|
||||
var value = status.Devices[0].value;
|
||||
|
||||
this.log('HomeSeer get power function succeeded: value=' + value );
|
||||
if( value == 0 )
|
||||
callback( null, 0 );
|
||||
else
|
||||
callback( null, 1 );
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
|
||||
setValue: function(level, callback) {
|
||||
var url = this.control_url + level;
|
||||
|
||||
this.log("Setting value to %s", level);
|
||||
|
||||
httpRequest(url, 'GET', function(error, response, body) {
|
||||
if (error) {
|
||||
this.log('HomeSeer set value function failed: %s', error.message);
|
||||
callback(error);
|
||||
}
|
||||
else {
|
||||
this.log('HomeSeer set value function succeeded!');
|
||||
callback();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
getValue: function(callback) {
|
||||
var url = this.status_url;
|
||||
|
||||
httpRequest(url, 'GET', function(error, response, body) {
|
||||
if (error) {
|
||||
this.log('HomeSeer get value function failed: %s', error.message);
|
||||
callback( error, 0 );
|
||||
}
|
||||
else {
|
||||
var status = JSON.parse( body );
|
||||
var value = status.Devices[0].value;
|
||||
|
||||
this.log('HomeSeer get value function succeeded: value=' + value );
|
||||
callback( null, value );
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
getServices: function() {
|
||||
var services = []
|
||||
|
||||
var informationService = new Service.AccessoryInformation();
|
||||
informationService
|
||||
.setCharacteristic(Characteristic.Manufacturer, "HomeSeer")
|
||||
.setCharacteristic(Characteristic.Model, this.model )
|
||||
.setCharacteristic(Characteristic.SerialNumber, "HS " + this.config.type + " ref " + this.ref);
|
||||
services.push( informationService );
|
||||
|
||||
|
||||
switch( this.config.type ) {
|
||||
case "Lightbulb": {
|
||||
var lightbulbService = new Service.Lightbulb();
|
||||
lightbulbService
|
||||
.getCharacteristic(Characteristic.On)
|
||||
.on('set', this.setPowerState.bind(this))
|
||||
.on('get', this.getPowerState.bind(this));
|
||||
|
||||
if( this.config.can_dim == null || this.config.can_dim == true ) {
|
||||
lightbulbService
|
||||
.addCharacteristic(new Characteristic.Brightness())
|
||||
.on('set', this.setValue.bind(this))
|
||||
.on('get', this.getValue.bind(this));
|
||||
}
|
||||
|
||||
services.push( lightbulbService );
|
||||
break;
|
||||
}
|
||||
case "Fan": {
|
||||
var fanService = new Service.Fan();
|
||||
fanService
|
||||
.getCharacteristic(Characteristic.On)
|
||||
.on('set', this.setPowerState.bind(this))
|
||||
.on('get', this.getPowerState.bind(this));
|
||||
services.push( fanService );
|
||||
break;
|
||||
}
|
||||
case "Switch": {
|
||||
var switchService = new Service.Switch();
|
||||
switchService
|
||||
.getCharacteristic(Characteristic.On)
|
||||
.on('set', this.setPowerState.bind(this))
|
||||
.on('get', this.getPowerState.bind(this));
|
||||
services.push( switchService );
|
||||
break;
|
||||
}
|
||||
case "Outlet": {
|
||||
var outletService = new Service.Outlet();
|
||||
outletService
|
||||
.getCharacteristic(Characteristic.On)
|
||||
.on('set', this.setPowerState.bind(this))
|
||||
.on('get', this.getPowerState.bind(this));
|
||||
services.push( outletService );
|
||||
break;
|
||||
}
|
||||
case "TemperatureSensor": {
|
||||
var temperatureSensorService = new Service.TemperatureSensor();
|
||||
temperatureSensorService
|
||||
.getCharacteristic(Characteristic.CurrentTemperature)
|
||||
.on('get', this.getValue.bind(this));
|
||||
services.push( temperatureSensorService );
|
||||
break;
|
||||
}
|
||||
case "ContactSensor": {
|
||||
var contactSensorService = new Service.ContactSensor();
|
||||
contactSensorService
|
||||
.getCharacteristic(Characteristic.ContactSensorState)
|
||||
.on('get', this.getPowerState.bind(this));
|
||||
services.push( contactSensorService );
|
||||
break;
|
||||
}
|
||||
case "MotionSensor": {
|
||||
var motionSensorService = new Service.MotionSensor();
|
||||
motionSensorService
|
||||
.getCharacteristic(Characteristic.MotionDetected)
|
||||
.on('get', this.getPowerState.bind(this));
|
||||
services.push( motionSensorService );
|
||||
break;
|
||||
}
|
||||
case "LeakSensor": {
|
||||
var leakSensorService = new Service.LeakSensor();
|
||||
leakSensorService
|
||||
.getCharacteristic(Characteristic.LeakDetected)
|
||||
.on('get', this.getPowerState.bind(this));
|
||||
services.push( leakSensorService );
|
||||
break;
|
||||
}
|
||||
case "LightSensor": {
|
||||
var lightSensorService = new Service.LightSensor();
|
||||
lightSensorService
|
||||
.getCharacteristic(Characteristic.CurrentAmbientLightLevel)
|
||||
.on('get', this.getValue.bind(this));
|
||||
services.push( lightSensorService );
|
||||
break;
|
||||
}
|
||||
case "OccupancySensor": {
|
||||
var occupancySensorService = new Service.OccupancySensor();
|
||||
motionSensorService
|
||||
.getCharacteristic(Characteristic.OccupancyDetected)
|
||||
.on('get', this.getPowerState.bind(this));
|
||||
services.push( occupancySensorService );
|
||||
break;
|
||||
}
|
||||
case "SmokeSensor": {
|
||||
var smokeSensorService = new Service.SmokeSensor();
|
||||
smokeSensorService
|
||||
.getCharacteristic(Characteristic.SmokeDetected)
|
||||
.on('get', this.getPowerState.bind(this));
|
||||
services.push( smokeSensorService );
|
||||
break;
|
||||
}
|
||||
case "Door": {
|
||||
var doorService = new Service.Door();
|
||||
doorService
|
||||
.getCharacteristic(Characteristic.CurrentPosition)
|
||||
.on('get', this.getValue.bind(this));
|
||||
doorService
|
||||
.getCharacteristic(Characteristic.TargetPosition)
|
||||
.on('set', this.setValue.bind(this));
|
||||
services.push( doorService );
|
||||
break;
|
||||
}
|
||||
default:{
|
||||
var lightbulbService = new Service.Lightbulb();
|
||||
lightbulbService
|
||||
.getCharacteristic(Characteristic.On)
|
||||
.on('set', this.setPowerState.bind(this))
|
||||
.on('get', this.getPowerState.bind(this));
|
||||
|
||||
lightbulbService
|
||||
.addCharacteristic(new Characteristic.Brightness())
|
||||
.on('set', this.setValue.bind(this))
|
||||
.on('get', this.getValue.bind(this));
|
||||
|
||||
services.push( lightbulbService );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.accessory = HomeSeerAccessory;
|
||||
module.exports.platform = HomeSeerPlatform;
|
||||
@@ -1,154 +1,156 @@
|
||||
{
|
||||
"bridge": {
|
||||
"name": "Homebridge",
|
||||
"username": "CC:22:3D:E3:CE:30",
|
||||
"port": 51826,
|
||||
"pin": "031-45-154"
|
||||
},
|
||||
"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",
|
||||
"name": "KNX",
|
||||
"knxd_ip": "192.168.178.205",
|
||||
"knxd_port": 6720,
|
||||
"accessories": [
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"description": "Only generic type knxdevice is supported, all previous knx types have been merged into that.",
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"services-description": "Services is an array, you CAN have multiple service types in one accessory, though it is not fully supported in many iOS HK apps, such as EVE and myTouchHome"
|
||||
},
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"name": "Office Temperature",
|
||||
"description": "iOS8.4.1 TemperatureSensor type, supports CurrentTemperature",
|
||||
"services": [
|
||||
{
|
||||
"type": "TemperatureSensor",
|
||||
"name": "Raumtemperatur",
|
||||
"CurrentTemperature": {
|
||||
"Listen": "3/3/44"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"name": "Office Window Lock",
|
||||
"services": [
|
||||
{
|
||||
"type": "LockMechanism",
|
||||
"description": "iOS8 Lock mechanism, Supports LockCurrentStateSecured0 OR LockCurrentState, LockTargetStateSecured0 OR LockTargetState, use depending if LOCKED is 0 or 1",
|
||||
"name": "Office Window Lock",
|
||||
"LockCurrentStateSecured0": {
|
||||
"Listen": "5/3/15"
|
||||
},
|
||||
"LockTargetStateSecured0": {
|
||||
"Listen": "5/3/15"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"description": "sample device with multiple services. Multiple services of different types are widely supported",
|
||||
"name": "Office",
|
||||
"services": [
|
||||
{
|
||||
"type": "Lightbulb",
|
||||
"name": "Office Lamp",
|
||||
"On": {
|
||||
"Set": "1/3/5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Thermostat",
|
||||
"description": "iOS8 Thermostat type, supports CurrentTemperature, TargetTemperature, CurrentHeatingCoolingState ",
|
||||
"name": "Raumtemperatur",
|
||||
"CurrentTemperature": {
|
||||
"Listen": "3/3/44"
|
||||
},
|
||||
"TargetTemperature": {
|
||||
"Set": "3/3/94"
|
||||
},
|
||||
"CurrentHeatingCoolingState": {
|
||||
"Listen": "3/3/64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "WindowCovering",
|
||||
"description": "iOS9 Window covering (blinds etc) type, still WIP",
|
||||
"name": "Blinds",
|
||||
"TargetPosition": {
|
||||
"Set": "1/2/3",
|
||||
"Listen": "1/2/4"
|
||||
},
|
||||
"CurrentPosition": {
|
||||
"Set": "1/3/1",
|
||||
"Listen": "1/3/2"
|
||||
},
|
||||
"PositionState": {
|
||||
"Listen": "2/7/1"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"description": "sample contact sensor device",
|
||||
"name": "Office Contact",
|
||||
"services": [
|
||||
{
|
||||
"type": "ContactSensor",
|
||||
"name": "Office Door",
|
||||
"ContactSensorState": {
|
||||
"Listen": "5/3/5"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"description": "sample garage door opener",
|
||||
"name": "Office Garage",
|
||||
"services": [
|
||||
{
|
||||
"type": "GarageDoorOpener",
|
||||
"name": "Office Garage Opener",
|
||||
"CurrentDoorState": {
|
||||
"Listen": "5/4/5"
|
||||
},
|
||||
"TargetDoorState": {
|
||||
"Listen": "5/4/6"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"accessories": []
|
||||
"bridge": {
|
||||
"name": "Homebridge",
|
||||
"username": "CC:22:3D:E3:CE:30",
|
||||
"port": 51826,
|
||||
"pin": "031-45-154"
|
||||
},
|
||||
"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 KNX.md file in folder platforms!",
|
||||
"platforms": [
|
||||
{
|
||||
"platform": "KNX",
|
||||
"name": "KNX",
|
||||
"knxd_ip": "192.168.178.205",
|
||||
"knxd_port": 6720,
|
||||
"accessories": [
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"description": "Only generic type knxdevice is supported, all previous knx types have been merged into that.",
|
||||
"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"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"services-description": "Services is an array, you CAN have multiple service types in one accessory, though it is not fully supported in many iOS HK apps, such as EVE and myTouchHome"
|
||||
},
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"name": "Office Temperature",
|
||||
"description": "iOS8.4.1 TemperatureSensor type, supports CurrentTemperature",
|
||||
"services": [
|
||||
{
|
||||
"type": "TemperatureSensor",
|
||||
"name": "Raumtemperatur",
|
||||
"CurrentTemperature": {
|
||||
"Listen": "3/3/44"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"name": "Office Window Lock",
|
||||
"services": [
|
||||
{
|
||||
"type": "LockMechanism",
|
||||
"description": "iOS8 Lock mechanism, Supports LockCurrentState, LockTargetState, append R to the addresses if LOCKED is 1",
|
||||
"name": "Office Window Lock",
|
||||
"LockCurrentState": {
|
||||
"Listen": "5/3/15R"
|
||||
},
|
||||
"LockTargetState": {
|
||||
"Listen": "5/3/16R"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"description": "sample device with multiple services. Multiple services of different types are widely supported",
|
||||
"name": "Office",
|
||||
"services": [
|
||||
{
|
||||
"type": "Lightbulb",
|
||||
"name": "Office Lamp",
|
||||
"On": {
|
||||
"Set": "1/3/5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Thermostat",
|
||||
"description": "iOS8 Thermostat type, supports CurrentTemperature, TargetTemperature, CurrentHeatingCoolingState ",
|
||||
"name": "Raumtemperatur",
|
||||
"CurrentTemperature": {
|
||||
"Listen": "3/3/44"
|
||||
},
|
||||
"TargetTemperature": {
|
||||
"Set": "3/3/94"
|
||||
},
|
||||
"CurrentHeatingCoolingState": {
|
||||
"Listen": "3/3/64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "WindowCovering",
|
||||
"description": "iOS9 Window covering (blinds etc) type, still WIP",
|
||||
"name": "Blinds",
|
||||
"TargetPosition": {
|
||||
"Set": "1/2/3",
|
||||
"Listen": "1/2/4"
|
||||
},
|
||||
"CurrentPosition": {
|
||||
"Set": "1/3/1",
|
||||
"Listen": "1/3/2"
|
||||
},
|
||||
"PositionState": {
|
||||
"Listen": "2/7/1"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"description": "sample contact sensor device",
|
||||
"name": "Office Contact",
|
||||
"services": [
|
||||
{
|
||||
"type": "ContactSensor",
|
||||
"name": "Office Door",
|
||||
"ContactSensorState": {
|
||||
"Listen": "5/3/5"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"accessory_type": "knxdevice",
|
||||
"description": "sample garage door opener",
|
||||
"name": "Office Garage",
|
||||
"services": [
|
||||
{
|
||||
"type": "GarageDoorOpener",
|
||||
"name": "Office Garage Opener",
|
||||
"CurrentDoorState": {
|
||||
"Listen": "5/4/5"
|
||||
},
|
||||
"TargetDoorState": {
|
||||
"Listen": "5/4/6"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"accessories": [
|
||||
|
||||
]
|
||||
}
|
||||
@@ -116,8 +116,8 @@ function groupsocketlisten(opts, callback) {
|
||||
}
|
||||
|
||||
|
||||
var registerSingleGA = function registerSingleGA (groupAddress, callback) {
|
||||
subscriptions.push({address: groupAddress, callback: callback });
|
||||
var registerSingleGA = function registerSingleGA (groupAddress, callback, reverse) {
|
||||
subscriptions.push({address: groupAddress, callback: callback, reverse:reverse });
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -143,7 +143,7 @@ var startMonitor = function startMonitor(opts) { // using { host: name-ip, port
|
||||
if (subscriptions[i].address === dest) {
|
||||
// found one, notify
|
||||
console.log('HIT: Write from '+src+' to '+dest+': '+val+' ['+type+']');
|
||||
subscriptions[i].callback(val, src, dest, type);
|
||||
subscriptions[i].callback(val, src, dest, type, subscriptions[i].reverse);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -156,7 +156,7 @@ var startMonitor = function startMonitor(opts) { // using { host: name-ip, port
|
||||
if (subscriptions[i].address === dest) {
|
||||
// found one, notify
|
||||
// console.log('HIT: Response from '+src+' to '+dest+': '+val+' ['+type+']');
|
||||
subscriptions[i].callback(val, src, dest, type);
|
||||
subscriptions[i].callback(val, src, dest, type, subscriptions[i].reverse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,13 +185,16 @@ var registerGA = function (groupAddresses, callback) {
|
||||
if (groupAddresses.constructor.toString().indexOf("Array") > -1) {
|
||||
// handle multiple addresses
|
||||
for (var i = 0; i < groupAddresses.length; i++) {
|
||||
if (groupAddresses[i]) { // do not bind empty addresses
|
||||
registerSingleGA (groupAddresses[i], callback);
|
||||
if (groupAddresses[i] && groupAddresses[i].match(/(\d*\/\d*\/\d*)/)) { // do not bind empty addresses or invalid addresses
|
||||
// clean the addresses
|
||||
registerSingleGA (groupAddresses[i].match(/(\d*\/\d*\/\d*)/)[0], callback,groupAddresses[i].match(/\d*\/\d*\/\d*(R)/) ? true:false );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// it's only one
|
||||
registerSingleGA (groupAddresses, callback);
|
||||
if (groupAddresses.match(/(\d*\/\d*\/\d*)/)) {
|
||||
registerSingleGA (groupAddresses.match(/(\d*\/\d*\/\d*)/)[0], callback, groupAddresses[i].match(/\d*\/\d*\/\d*(R)/) ? true:false);
|
||||
}
|
||||
}
|
||||
// console.log("listeners now: " + subscriptions.length);
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ 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",
|
||||
"name": "beer tap thermostat",
|
||||
"CHARACTERISTIC1": {
|
||||
"Set": "1/1/6",
|
||||
"Listen": [
|
||||
@@ -68,11 +68,54 @@ Two kinds of addresses are supported: `"Set":"1/2/3"` is a writable group addres
|
||||
`"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 *KNX read requests* to ALL addresses listed in `Set:` and in `Listen:`
|
||||
|
||||
|
||||
# Supported Services and their characteristics
|
||||
For two characteristics there are additional minValue and maxValue attributes. These are CurrentTemperature and TargetTemperature, and are used in TemperatureSensor and Thermostat.
|
||||
|
||||
So the charcteristic section may look like:
|
||||
|
||||
````json
|
||||
{
|
||||
"type": "Thermostat",
|
||||
"description": "Sample thermostat",
|
||||
"name": "We need a name for each service, though it usually shows only if multiple services are present in one accessory",
|
||||
"CurrentTemperature": {
|
||||
"Set": "1/1/6",
|
||||
"Listen": [
|
||||
"1/1/63"
|
||||
],
|
||||
"minValue": -18,
|
||||
"maxValue": 30
|
||||
},
|
||||
"TargetTemperature": {
|
||||
"Set": "1/1/62",
|
||||
"Listen": [
|
||||
"1/1/64"
|
||||
],
|
||||
"minValue": -4,
|
||||
"maxValue": 12
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
|
||||
## reversal of values for characteristics
|
||||
In general, all DPT1 types can be reversed. If you need a 1 for "contact" of a contact senser, you can append an "R" to the group address.
|
||||
Likewise, all percentages of DPT5 can be reversed, if you need a 100% (=255) for window closed, append an "R" to the group address. Do not forget the listening addresses!
|
||||
````json
|
||||
{
|
||||
"type": "ContactSensor",
|
||||
"description": "Sample ContactSensor with 1 as contact (0 is Apple's default)",
|
||||
"name": "WindowContact1",
|
||||
"ContactSensorState": {
|
||||
"Listen": [
|
||||
"1/1/100R"
|
||||
]
|
||||
}
|
||||
}
|
||||
````
|
||||
# Supported Services and their characteristics
|
||||
## ContactSensor
|
||||
- ContactSensorState: DPT 1.002, 0 as contact **OR**
|
||||
- ContactSensorStateContact1: DPT 1.002, 1 as contact
|
||||
- ContactSensorState: DPT 1.002, 0 as contact
|
||||
- ~~ContactSensorStateContact1: DPT 1.002, 1 as contact~~
|
||||
|
||||
- StatusActive: DPT 1.011, 1 as true
|
||||
- StatusFault: DPT 1.011, 1 as true
|
||||
@@ -113,10 +156,10 @@ Two kinds of addresses are supported: `"Set":"1/2/3"` is a writable group addres
|
||||
- CurrentAmbientLightLevel: DPT 9.004, 0 to 100000 Lux
|
||||
|
||||
## LockMechanism (This is poorly mapped!)
|
||||
- LockCurrentState: DPT 1, 1 as secured **OR (but not both:)**
|
||||
- LockCurrentStateSecured0: DPT 1, 0 as secured
|
||||
- LockTargetState: DPT 1, 1 as secured **OR**
|
||||
- LockTargetStateSecured0: DPT 1, 0 as secured
|
||||
- LockCurrentState: DPT 1, 1 as secured
|
||||
- ~~LockCurrentStateSecured0: DPT 1, 0 as secured~~
|
||||
- LockTargetState: DPT 1, 1 as secured
|
||||
- ~~LockTargetStateSecured0: DPT 1, 0 as secured~~
|
||||
|
||||
*ToDo here: correction of mappings, HomeKit reqires lock states UNSECURED=0, SECURED=1, JAMMED = 2, UNKNOWN=3*
|
||||
|
||||
@@ -136,11 +179,11 @@ Two kinds of addresses are supported: `"Set":"1/2/3"` is a writable group addres
|
||||
- On: DPT 1.001, 1 as on, 0 as off
|
||||
|
||||
## TemperatureSensor
|
||||
- CurrentTemperature: DPT9.001 in °C [listen only]
|
||||
- CurrentTemperature: DPT9.001 in °C [listen only]
|
||||
|
||||
## Thermostat
|
||||
- CurrentTemperature: DPT9.001 in °C [listen only]
|
||||
- TargetTemperature: DPT9.001, values 0..40°C only, all others are ignored
|
||||
- CurrentTemperature: DPT9.001 in °C [listen only], -40 to 80°C if not overriden as shown above
|
||||
- TargetTemperature: DPT9.001, values 0..40°C only, all others are ignored
|
||||
- CurrentHeatingCoolingState: DPT20.102 HVAC, because of the incompatible mapping only off and heating (=auto) are shown, [listen only]
|
||||
- TargetHeatingCoolingState: DPT20.102 HVAC, as above
|
||||
|
||||
@@ -152,7 +195,7 @@ Two kinds of addresses are supported: `"Set":"1/2/3"` is a writable group addres
|
||||
## WindowCovering
|
||||
- CurrentPosition: DPT5 percentage
|
||||
- TargetPosition: DPT5 percentage
|
||||
- PositionState: DPT5 value [listen only]
|
||||
- PositionState: DPT5 value [listen only: 0 Closing, 1 Opening, 2 Stopped]
|
||||
|
||||
### not yet supported
|
||||
- HoldPosition
|
||||
|
||||
@@ -44,7 +44,11 @@ NestPlatform.prototype = {
|
||||
|
||||
function NestThermostatAccessory(log, name, device, deviceId) {
|
||||
// device info
|
||||
this.name = name;
|
||||
if (name) {
|
||||
this.name = name;
|
||||
} else {
|
||||
this.name = "Nest";
|
||||
}
|
||||
this.model = device.model_version;
|
||||
this.serial = device.serial_number;
|
||||
this.deviceId = deviceId;
|
||||
@@ -390,4 +394,4 @@ NestThermostatAccessory.prototype = {
|
||||
}
|
||||
|
||||
module.exports.accessory = NestThermostatAccessory;
|
||||
module.exports.platform = NestPlatform;
|
||||
module.exports.platform = NestPlatform;
|
||||
|
||||
@@ -23,6 +23,7 @@ function YamahaAVRPlatform(log, config){
|
||||
this.setMainInputTo = config["setMainInputTo"];
|
||||
this.expectedDevices = config["expected_devices"] || 100;
|
||||
this.discoveryTimeout = config["discovery_timeout"] || 30;
|
||||
this.manualAddresses = config["manual_addresses"] || {};
|
||||
this.browser = mdns.createBrowser(mdns.tcp('http'), {resolverSequence: sequence});
|
||||
}
|
||||
|
||||
@@ -75,25 +76,44 @@ YamahaAVRPlatform.prototype = {
|
||||
var accessories = [];
|
||||
var timer, timeElapsed = 0, checkCyclePeriod = 5000;
|
||||
|
||||
browser.on('serviceUp', function(service){
|
||||
// Hmm... seems we need to prevent double-listing via manual and Bonjour...
|
||||
var sysIds = {};
|
||||
|
||||
var setupFromService = function(service){
|
||||
var name = service.name;
|
||||
//console.log('Found HTTP service "' + name + '"');
|
||||
// We can't tell just from mdns if this is an AVR...
|
||||
if (service.port != 80) return; // yamaha-nodejs assumes this, so finding one on another port wouldn't do any good anyway.
|
||||
var yamaha = new Yamaha(service.host);
|
||||
yamaha.getSystemConfig().then(function(sysConfig){
|
||||
var sysModel = sysConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0];
|
||||
var sysId = sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0];
|
||||
that.log("Found Yamaha " + sysModel + " - " + sysId + ", \"" + name + "\"");
|
||||
var accessory = new YamahaAVRAccessory(that.log, that.config, service, yamaha, sysConfig);
|
||||
accessories.push(accessory);
|
||||
if(accessories.length >= this.expectedDevices)
|
||||
timeoutFunction(); // We're done, call the timeout function now.
|
||||
//callback([accessory]);
|
||||
}, function(err){
|
||||
return;
|
||||
yamaha.getSystemConfig().then(
|
||||
function(sysConfig){
|
||||
var sysModel = sysConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0];
|
||||
var sysId = sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0];
|
||||
if(sysIds[sysId]){
|
||||
this.log("WARN: Got multiple systems with ID " + sysId + "! Omitting duplicate!");
|
||||
return;
|
||||
}
|
||||
sysIds[sysId] = true;
|
||||
this.log("Found Yamaha " + sysModel + " - " + sysId + ", \"" + name + "\"");
|
||||
var accessory = new YamahaAVRAccessory(this.log, this.config, name, yamaha, sysConfig);
|
||||
accessories.push(accessory);
|
||||
if(accessories.length >= this.expectedDevices)
|
||||
timeoutFunction(); // We're done, call the timeout function now.
|
||||
}.bind(this)
|
||||
);
|
||||
}.bind(this);
|
||||
|
||||
// process manually specified devices...
|
||||
for(var key in this.manualAddresses){
|
||||
if(!this.manualAddresses.hasOwnProperty(key)) continue;
|
||||
setupFromService({
|
||||
name: key,
|
||||
host: this.manualAddresses[key],
|
||||
port: 80
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
browser.on('serviceUp', setupFromService);
|
||||
browser.start();
|
||||
|
||||
// The callback can only be called once...so we'll have to find as many as we can
|
||||
@@ -119,15 +139,15 @@ YamahaAVRPlatform.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
function YamahaAVRAccessory(log, config, mdnsService, yamaha, sysConfig) {
|
||||
function YamahaAVRAccessory(log, config, name, yamaha, sysConfig) {
|
||||
this.log = log;
|
||||
this.config = config;
|
||||
this.mdnsService = mdnsService;
|
||||
this.yamaha = yamaha;
|
||||
this.sysConfig = sysConfig;
|
||||
|
||||
this.name = mdnsService.name;
|
||||
this.serviceName = mdnsService.name + " Speakers";
|
||||
this.nameSuffix = config["name_suffix"] || " Speakers";
|
||||
this.name = name;
|
||||
this.serviceName = name + this.nameSuffix;
|
||||
this.setMainInputTo = config["setMainInputTo"];
|
||||
this.playVolume = this.config["play_volume"];
|
||||
this.minVolume = config["min_volume"] || -50.0;
|
||||
|
||||
Reference in New Issue
Block a user