mirror of
https://github.com/mtan93/homebridge.git
synced 2026-03-16 21:03:09 +00:00
@@ -52,14 +52,19 @@ You'll also need some patience, as Siri can be very strict about sentence struct
|
||||
|
||||
# Getting Started
|
||||
|
||||
OK, if you're still excited enough about ordering Siri to make your coffee (which, who wouldn't be!) then here's how to set things up. First, clone this repo:
|
||||
OK, if you're still excited enough about ordering Siri to make your coffee (which, who wouldn't be!) then here's how to set things up.
|
||||
|
||||
**Note:** If you're running on Linux, you'll need to make sure you have the `libavahi-compat-libdnssd-dev` package installed.
|
||||
|
||||
First, clone this repo:
|
||||
|
||||
$ git clone https://github.com/nfarina/homebridge.git
|
||||
$ cd homebridge
|
||||
$ npm install
|
||||
|
||||
**Node**: You'll need to have NodeJS version 0.12.x or better installed for required submodule `HAP-NodeJS` to load.
|
||||
**Note**: You'll need to have NodeJS version 0.12.x or better installed for required submodule `HAP-NodeJS` to load.
|
||||
|
||||
|
||||
Now you should be able to run the homebridge server:
|
||||
|
||||
$ cd homebridge
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,126 +0,0 @@
|
||||
# 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!
|
||||
|
||||
@@ -89,14 +89,19 @@
|
||||
"delay": 30,
|
||||
"repeat": 3,
|
||||
"zones":["Kitchen Lamp","Bedroom Lamp","Living Room Lamp","Hallway Lamp"]
|
||||
},
|
||||
{
|
||||
},
|
||||
{
|
||||
"platform": "HomeAssistant",
|
||||
"name": "HomeAssistant",
|
||||
"host": "http://192.168.1.10:8123",
|
||||
"password": "XXXXX",
|
||||
"supported_types": ["light", "switch", "media_player", "scene"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"platform": "LIFx",
|
||||
"name": "LIFx",
|
||||
"access_token": "XXXXXXXX generate at https://cloud.lifx.com/settings"
|
||||
}
|
||||
],
|
||||
|
||||
"accessories": [
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"color": "0.10.x",
|
||||
"eibd": "^0.3.1",
|
||||
"elkington": "kevinohara80/elkington",
|
||||
"hap-nodejs": "git+https://github.com/KhaosT/HAP-NodeJS#0030b35856e04ee2b42f0d05839feaa5c44cbd1f",
|
||||
"hap-nodejs": "git+https://github.com/KhaosT/HAP-NodeJS#4650e771f356a220868d873d16564a6be6603ff7",
|
||||
"harmonyhubjs-client": "^1.1.4",
|
||||
"harmonyhubjs-discover": "git+https://github.com/swissmanu/harmonyhubjs-discover.git",
|
||||
"lifx-api": "^1.0.1",
|
||||
|
||||
253
platforms/FibaroHC2.js
Normal file
253
platforms/FibaroHC2.js
Normal file
@@ -0,0 +1,253 @@
|
||||
// Fibaro Home Center 2 Platform Shim for HomeBridge
|
||||
//
|
||||
// Remember to add platform to config.json. Example:
|
||||
// "platforms": [
|
||||
// {
|
||||
// "platform": "FibaroHC2",
|
||||
// "name": "FibaroHC2",
|
||||
// "host": "PUT IP ADDRESS OF YOUR HC2 HERE",
|
||||
// "username": "PUT USERNAME OF YOUR HC2 HERE",
|
||||
// "password": "PUT PASSWORD OF YOUR HC2 HERE"
|
||||
// }
|
||||
// ],
|
||||
//
|
||||
// When you attempt to add a device, it will ask for a "PIN code".
|
||||
// The default code for all HomeBridge accessories is 031-45-154.
|
||||
|
||||
var types = require("HAP-NodeJS/accessories/types.js");
|
||||
var Service = require("HAP-NodeJS").Service;
|
||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
||||
var request = require("request");
|
||||
|
||||
function FibaroHC2Platform(log, config){
|
||||
this.log = log;
|
||||
this.host = config["host"];
|
||||
this.username = config["username"];
|
||||
this.password = config["password"];
|
||||
this.auth = "Basic " + new Buffer(this.username + ":" + this.password).toString("base64");
|
||||
this.url = "http://"+this.host+"/api/devices";
|
||||
|
||||
startPollingUpdate( this );
|
||||
}
|
||||
|
||||
FibaroHC2Platform.prototype = {
|
||||
accessories: function(callback) {
|
||||
this.log("Fetching Fibaro Home Center devices...");
|
||||
|
||||
var that = this;
|
||||
var foundAccessories = [];
|
||||
|
||||
request.get({
|
||||
url: this.url,
|
||||
headers : {
|
||||
"Authorization" : this.auth
|
||||
},
|
||||
json: true
|
||||
}, function(err, response, json) {
|
||||
if (!err && response.statusCode == 200) {
|
||||
if (json != undefined) {
|
||||
json.map(function(s) {
|
||||
that.log("Found: " + s.type);
|
||||
if (s.visible == true) {
|
||||
var accessory = null;
|
||||
if (s.type == "com.fibaro.multilevelSwitch")
|
||||
accessory = new FibaroAccessory(new Service.Lightbulb(s.name), [Characteristic.On, Characteristic.Brightness]);
|
||||
else if (s.type == "com.fibaro.FGRM222" || s.type == "com.fibaro.FGR221")
|
||||
accessory = new FibaroAccessory(new Service.WindowCovering(s.name), [Characteristic.CurrentPosition, Characteristic.TargetPosition, Characteristic.PositionState]);
|
||||
else if (s.type == "com.fibaro.binarySwitch" || s.type == "com.fibaro.developer.bxs.virtualBinarySwitch")
|
||||
accessory = new FibaroAccessory(new Service.Switch(s.name), [Characteristic.On]);
|
||||
else if (s.type == "com.fibaro.FGMS001" || s.type == "com.fibaro.motionSensor")
|
||||
accessory = new FibaroAccessory(new Service.MotionSensor(s.name), [Characteristic.MotionDetected]);
|
||||
else if (s.type == "com.fibaro.temperatureSensor")
|
||||
accessory = new FibaroAccessory(new Service.TemperatureSensor(s.name), [Characteristic.CurrentTemperature]);
|
||||
else if (s.type == "com.fibaro.doorSensor")
|
||||
accessory = new FibaroAccessory(new Service.ContactSensor(s.name), [Characteristic.ContactSensorState]);
|
||||
else if (s.type == "com.fibaro.lightSensor")
|
||||
accessory = new FibaroAccessory(new Service.LightSensor(s.name), [Characteristic.CurrentAmbientLightLevel]);
|
||||
else if (s.type == "com.fibaro.FGWP101")
|
||||
accessory = new FibaroAccessory(new Service.Outlet(s.name), [Characteristic.On, Characteristic.OutletInUse]);
|
||||
if (accessory != null) {
|
||||
accessory.getServices = function() {
|
||||
return that.getServices(accessory);
|
||||
};
|
||||
accessory.platform = that;
|
||||
accessory.remoteAccessory = s;
|
||||
accessory.id = s.id;
|
||||
accessory.name = s.name;
|
||||
accessory.model = s.type;
|
||||
accessory.manufacturer = "Fibaro";
|
||||
accessory.serialNumber = "<unknown>";
|
||||
foundAccessories.push(accessory);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
callback(foundAccessories);
|
||||
} else {
|
||||
that.log("There was a problem connecting with FibaroHC2.");
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
command: function(c,value, that) {
|
||||
var url = "http://"+this.host+"/api/devices/"+that.id+"/action/"+c;
|
||||
var body = value != undefined ? JSON.stringify({
|
||||
"args": [ value ]
|
||||
}) : null;
|
||||
var method = "post";
|
||||
request({
|
||||
url: url,
|
||||
body: body,
|
||||
method: method,
|
||||
headers: {
|
||||
"Authorization" : this.auth
|
||||
},
|
||||
}, function(err, response) {
|
||||
if (err) {
|
||||
that.platform.log("There was a problem sending command " + c + " to" + that.name);
|
||||
that.platform.log(url);
|
||||
} else {
|
||||
that.platform.log(that.name + " sent command " + c);
|
||||
that.platform.log(url);
|
||||
}
|
||||
});
|
||||
},
|
||||
getAccessoryValue: function(callback, returnBoolean, homebridgeAccessory, powerValue) {
|
||||
var url = "http://"+homebridgeAccessory.platform.host+"/api/devices/"+homebridgeAccessory.id+"/properties/";
|
||||
if (powerValue)
|
||||
url = url + "power";
|
||||
else
|
||||
url = url + "value";
|
||||
|
||||
request.get({
|
||||
headers : {
|
||||
"Authorization" : homebridgeAccessory.platform.auth
|
||||
},
|
||||
json: true,
|
||||
url: url
|
||||
}, function(err, response, json) {
|
||||
homebridgeAccessory.platform.log(url);
|
||||
if (!err && response.statusCode == 200) {
|
||||
if (powerValue) {
|
||||
callback(undefined, parseFloat(json.value) > 1.0 ? true : false);
|
||||
} else if (returnBoolean)
|
||||
callback(undefined, json.value == 0 ? 0 : 1);
|
||||
else
|
||||
callback(undefined, json.value);
|
||||
} else {
|
||||
homebridgeAccessory.platform.log("There was a problem getting value from" + homebridgeAccessory.id);
|
||||
}
|
||||
})
|
||||
},
|
||||
getInformationService: function(homebridgeAccessory) {
|
||||
var informationService = new Service.AccessoryInformation();
|
||||
informationService
|
||||
.setCharacteristic(Characteristic.Name, homebridgeAccessory.name)
|
||||
.setCharacteristic(Characteristic.Manufacturer, homebridgeAccessory.manufacturer)
|
||||
.setCharacteristic(Characteristic.Model, homebridgeAccessory.model)
|
||||
.setCharacteristic(Characteristic.SerialNumber, homebridgeAccessory.serialNumber);
|
||||
return informationService;
|
||||
},
|
||||
bindCharacteristicEvents: function(characteristic, homebridgeAccessory) {
|
||||
var onOff = characteristic.props.format == "bool" ? true : false;
|
||||
var readOnly = true;
|
||||
for (var i = 0; i < characteristic.props.perms.length; i++)
|
||||
if (characteristic.props.perms[i] == "pw")
|
||||
readOnly = false;
|
||||
var powerValue = (characteristic.UUID == "00000026-0000-1000-8000-0026BB765291") ? true : false;
|
||||
subscribeUpdate(characteristic, homebridgeAccessory, onOff);
|
||||
if (!readOnly) {
|
||||
characteristic
|
||||
.on('set', function(value, callback, context) {
|
||||
if( context !== 'fromFibaro' ) {
|
||||
if (onOff)
|
||||
homebridgeAccessory.platform.command(value == 0 ? "turnOff": "turnOn", null, homebridgeAccessory);
|
||||
else
|
||||
homebridgeAccessory.platform.command("setValue", value, homebridgeAccessory);
|
||||
}
|
||||
callback();
|
||||
}.bind(this) );
|
||||
}
|
||||
characteristic
|
||||
.on('get', function(callback) {
|
||||
homebridgeAccessory.platform.getAccessoryValue(callback, onOff, homebridgeAccessory, powerValue);
|
||||
}.bind(this) );
|
||||
},
|
||||
getServices: function(homebridgeAccessory) {
|
||||
var informationService = homebridgeAccessory.platform.getInformationService(homebridgeAccessory);
|
||||
for (var i=0; i < homebridgeAccessory.characteristics.length; i++) {
|
||||
var characteristic = homebridgeAccessory.controlService.getCharacteristic(homebridgeAccessory.characteristics[i]);
|
||||
if (characteristic == undefined)
|
||||
characteristic = homebridgeAccessory.controlService.addCharacteristic(homebridgeAccessory.characteristics[i]);
|
||||
homebridgeAccessory.platform.bindCharacteristicEvents(characteristic, homebridgeAccessory);
|
||||
}
|
||||
|
||||
return [informationService, homebridgeAccessory.controlService];
|
||||
}
|
||||
}
|
||||
|
||||
function FibaroAccessory(controlService, characteristics) {
|
||||
this.controlService = controlService;
|
||||
this.characteristics = characteristics;
|
||||
}
|
||||
|
||||
var lastPoll=0;
|
||||
var pollingUpdateRunning = false;
|
||||
|
||||
function startPollingUpdate( platform )
|
||||
{
|
||||
if( pollingUpdateRunning )
|
||||
return;
|
||||
pollingUpdateRunning = true;
|
||||
|
||||
var updateUrl = "http://"+platform.host+"/api/refreshStates?last="+lastPoll;
|
||||
|
||||
request.get({
|
||||
url: updateUrl,
|
||||
headers : {
|
||||
"Authorization" : platform.auth
|
||||
},
|
||||
json: true
|
||||
}, function(err, response, json) {
|
||||
if (!err && response.statusCode == 200) {
|
||||
if (json != undefined) {
|
||||
lastPoll = json.last;
|
||||
if (json.changes != undefined) {
|
||||
json.changes.map(function(s) {
|
||||
if (s.value != undefined) {
|
||||
|
||||
var value=parseInt(s.value);
|
||||
if (isNaN(value))
|
||||
value=(s.value === "true");
|
||||
for (i=0;i<updateSubscriptions.length; i++) {
|
||||
var subscription = updateSubscriptions[i];
|
||||
if (subscription.id == s.id) {
|
||||
if (s.power != undefined && subscription.characteristic.UUID == "00000026-0000-1000-8000-0026BB765291") {
|
||||
subscription.characteristic.setValue(parseFloat(s.power) > 1.0 ? true : false, undefined, 'fromFibaro');
|
||||
} else if ((subscription.onOff && typeof(value) == "boolean") || !subscription.onOff)
|
||||
subscription.characteristic.setValue(value, undefined, 'fromFibaro');
|
||||
else
|
||||
subscription.characteristic.setValue(value == 0 ? false : true, undefined, 'fromFibaro');
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
platform.log("There was a problem connecting with FibaroHC2.");
|
||||
}
|
||||
pollingUpdateRunning = false;
|
||||
setTimeout( function(){startPollingUpdate(platform)}, 2000 );
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
var updateSubscriptions = [];
|
||||
function subscribeUpdate(characteristic, accessory, onOff)
|
||||
{
|
||||
// TODO: optimized management of updateSubscription data structure (no array with sequential access)
|
||||
updateSubscriptions.push({ 'id': accessory.id, 'characteristic': characteristic, 'accessory': accessory, 'onOff': onOff });
|
||||
}
|
||||
|
||||
module.exports.platform = FibaroHC2Platform;
|
||||
@@ -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')
|
||||
|
||||
156
platforms/KNX-sample-config.json
Normal file
156
platforms/KNX-sample-config.json
Normal file
@@ -0,0 +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 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);
|
||||
};
|
||||
|
||||
213
platforms/KNX.md
Normal file
213
platforms/KNX.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# 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.
|
||||
````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
|
||||
````json
|
||||
{
|
||||
"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:
|
||||
````json
|
||||
{
|
||||
"type": "SERVICENAME",
|
||||
"description": "This is just for you to remember things",
|
||||
"name": "beer tap thermostat",
|
||||
"CHARACTERISTIC1": {
|
||||
"Set": "1/1/6",
|
||||
"Listen": [
|
||||
"1/1/63"
|
||||
]
|
||||
},
|
||||
"CHARACTERISTIC2": {
|
||||
"Set": "1/1/62",
|
||||
"Listen": [
|
||||
"1/1/64"
|
||||
]
|
||||
}
|
||||
}
|
||||
````
|
||||
`CHARACTERISTICx` 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 *KNX read requests* to ALL addresses listed in `Set:` and in `Listen:`
|
||||
|
||||
|
||||
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
|
||||
- ~~ContactSensorStateContact1: DPT 1.002, 1 as contact~~
|
||||
|
||||
- StatusActive: DPT 1.011, 1 as true
|
||||
- StatusFault: DPT 1.011, 1 as true
|
||||
- StatusTampered: DPT 1.011, 1 as true
|
||||
- StatusLowBattery: DPT 1.011, 1 as true
|
||||
|
||||
## GarageDoorOpener
|
||||
- CurrentDoorState: DPT5 integer value in range 0..4
|
||||
// Characteristic.CurrentDoorState.OPEN = 0;
|
||||
// Characteristic.CurrentDoorState.CLOSED = 1;
|
||||
// Characteristic.CurrentDoorState.OPENING = 2;
|
||||
// Characteristic.CurrentDoorState.CLOSING = 3;
|
||||
// Characteristic.CurrentDoorState.STOPPED = 4;
|
||||
|
||||
- TargetDoorState: DPT5 integer value in range 0..1
|
||||
// Characteristic.TargetDoorState.OPEN = 0;
|
||||
// Characteristic.TargetDoorState.CLOSED = 1;
|
||||
|
||||
- ObstructionDetected: DPT1, 1 as true
|
||||
|
||||
- LockCurrentState: DPT5 integer value in range 0..3
|
||||
// Characteristic.LockCurrentState.UNSECURED = 0;
|
||||
// Characteristic.LockCurrentState.SECURED = 1;
|
||||
// Characteristic.LockCurrentState.JAMMED = 2;
|
||||
// Characteristic.LockCurrentState.UNKNOWN = 3;
|
||||
|
||||
- LockTargetState: DPT5 integer value in range 0..1
|
||||
// Characteristic.LockTargetState.UNSECURED = 0;
|
||||
// Characteristic.LockTargetState.SECURED = 1;
|
||||
|
||||
|
||||
|
||||
## Lightbulb
|
||||
- On: DPT 1.001, 1 as on, 0 as off
|
||||
- Brightness: DPT5.001 percentage, 100% (=255) the brightest
|
||||
|
||||
## LightSensor
|
||||
- CurrentAmbientLightLevel: DPT 9.004, 0 to 100000 Lux
|
||||
|
||||
## LockMechanism (This is poorly mapped!)
|
||||
- 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*
|
||||
|
||||
## MotionSensor
|
||||
- MotionDetected: DPT 1.002, 1 as motion detected
|
||||
|
||||
- StatusActive: DPT 1.011, 1 as true
|
||||
- StatusFault: DPT 1.011, 1 as true
|
||||
- StatusTampered: DPT 1.011, 1 as true
|
||||
- StatusLowBattery: DPT 1.011, 1 as true
|
||||
|
||||
## Outlet
|
||||
- On: DPT 1.001, 1 as on, 0 as off
|
||||
- OutletInUse: DPT 1.011, 1 as on, 0 as off
|
||||
|
||||
## Switch
|
||||
- On: DPT 1.001, 1 as on, 0 as off
|
||||
|
||||
## TemperatureSensor
|
||||
- CurrentTemperature: DPT9.001 in °C [listen only]
|
||||
|
||||
## Thermostat
|
||||
- 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
|
||||
|
||||
## Window
|
||||
- CurrentPosition: DPT5.001 percentage
|
||||
- TargetPosition: DPT5.001 percentage
|
||||
- PositionState: DPT5.005 value [listen only: 0 Increasing, 1 Decreasing, 2 Stopped]
|
||||
|
||||
## WindowCovering
|
||||
- CurrentPosition: DPT5 percentage
|
||||
- TargetPosition: DPT5 percentage
|
||||
- PositionState: DPT5 value [listen only: 0 Closing, 1 Opening, 2 Stopped]
|
||||
|
||||
### not yet supported
|
||||
- HoldPosition
|
||||
- TargetHorizontalTiltAngle
|
||||
- TargetVerticalTiltAngle
|
||||
- CurrentHorizontalTiltAngle
|
||||
- CurrentVerticalTiltAngle
|
||||
- ObstructionDetected
|
||||
|
||||
|
||||
|
||||
|
||||
# DISCLAIMER
|
||||
**This is work in progress!**
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
var types = require("HAP-NodeJS/accessories/types.js");
|
||||
var inherits = require('util').inherits;
|
||||
var debug = require('debug')('YamahaAVR');
|
||||
var Service = require("HAP-NodeJS").Service;
|
||||
var Characteristic = require("HAP-NodeJS").Characteristic;
|
||||
var Yamaha = require('yamaha-nodejs');
|
||||
var Q = require('q');
|
||||
var mdns = require('mdns');
|
||||
//workaround for raspberry pi
|
||||
var sequence = [
|
||||
@@ -12,10 +17,53 @@ function YamahaAVRPlatform(log, config){
|
||||
this.log = log;
|
||||
this.config = config;
|
||||
this.playVolume = config["play_volume"];
|
||||
this.minVolume = config["min_volume"] || -50.0;
|
||||
this.maxVolume = config["max_volume"] || -20.0;
|
||||
this.gapVolume = this.maxVolume - this.minVolume;
|
||||
this.setMainInputTo = config["setMainInputTo"];
|
||||
this.expectedDevices = config["expected_devices"] || 100;
|
||||
this.discoveryTimeout = config["discovery_timeout"] || 30;
|
||||
this.browser = mdns.createBrowser(mdns.tcp('http'), {resolverSequence: sequence});
|
||||
}
|
||||
|
||||
// Custom Characteristics and service...
|
||||
|
||||
YamahaAVRPlatform.AudioVolume = function() {
|
||||
Characteristic.call(this, 'Audio Volume', '00001001-0000-1000-8000-135D67EC4377');
|
||||
this.format = 'uint8';
|
||||
this.unit = 'percentage';
|
||||
this.maximumValue = 100;
|
||||
this.minimumValue = 0;
|
||||
this.stepValue = 1;
|
||||
this.readable = true;
|
||||
this.writable = true;
|
||||
this.supportsEventNotification = true;
|
||||
this.value = this.getDefaultValue();
|
||||
};
|
||||
inherits(YamahaAVRPlatform.AudioVolume, Characteristic);
|
||||
|
||||
YamahaAVRPlatform.Muting = function() {
|
||||
Characteristic.call(this, 'Muting', '00001002-0000-1000-8000-135D67EC4377');
|
||||
this.format = 'bool';
|
||||
this.readable = true;
|
||||
this.writable = true;
|
||||
this.supportsEventNotification = true;
|
||||
this.value = this.getDefaultValue();
|
||||
};
|
||||
inherits(YamahaAVRPlatform.Muting, Characteristic);
|
||||
|
||||
YamahaAVRPlatform.AudioDeviceService = function(displayName, subtype) {
|
||||
Service.call(this, displayName, '00000001-0000-1000-8000-135D67EC4377', subtype);
|
||||
|
||||
// Required Characteristics
|
||||
this.addCharacteristic(YamahaAVRPlatform.AudioVolume);
|
||||
|
||||
// Optional Characteristics
|
||||
this.addOptionalCharacteristic(YamahaAVRPlatform.Muting);
|
||||
};
|
||||
inherits(YamahaAVRPlatform.AudioDeviceService, Service);
|
||||
|
||||
|
||||
YamahaAVRPlatform.prototype = {
|
||||
accessories: function(callback) {
|
||||
this.log("Getting Yamaha AVR devices.");
|
||||
@@ -24,7 +72,9 @@ YamahaAVRPlatform.prototype = {
|
||||
var browser = this.browser;
|
||||
browser.stop();
|
||||
browser.removeAllListeners('serviceUp'); // cleanup listeners
|
||||
|
||||
var accessories = [];
|
||||
var timer, timeElapsed = 0, checkCyclePeriod = 5000;
|
||||
|
||||
browser.on('serviceUp', function(service){
|
||||
var name = service.name;
|
||||
//console.log('Found HTTP service "' + name + '"');
|
||||
@@ -36,12 +86,36 @@ YamahaAVRPlatform.prototype = {
|
||||
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);
|
||||
callback([accessory]);
|
||||
accessories.push(accessory);
|
||||
if(accessories.length >= this.expectedDevices)
|
||||
timeoutFunction(); // We're done, call the timeout function now.
|
||||
//callback([accessory]);
|
||||
}, function(err){
|
||||
return;
|
||||
})
|
||||
});
|
||||
});
|
||||
browser.start();
|
||||
|
||||
// The callback can only be called once...so we'll have to find as many as we can
|
||||
// in a fixed time and then call them in.
|
||||
var timeoutFunction = function(){
|
||||
if(accessories.length >= that.expectedDevices){
|
||||
clearTimeout(timer);
|
||||
} else {
|
||||
timeElapsed += checkCyclePeriod;
|
||||
if(timeElapsed > that.discoveryTimeout * 1000){
|
||||
that.log("Waited " + that.discoveryTimeout + " seconds, stopping discovery.");
|
||||
} else {
|
||||
timer = setTimeout(timeoutFunction, checkCyclePeriod);
|
||||
return;
|
||||
}
|
||||
}
|
||||
browser.stop();
|
||||
browser.removeAllListeners('serviceUp');
|
||||
that.log("Discovery finished, found " + accessories.length + " Yamaha AVR devices.");
|
||||
callback(accessories);
|
||||
};
|
||||
timer = setTimeout(timeoutFunction, checkCyclePeriod);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -56,6 +130,9 @@ function YamahaAVRAccessory(log, config, mdnsService, yamaha, sysConfig) {
|
||||
this.serviceName = mdnsService.name + " Speakers";
|
||||
this.setMainInputTo = config["setMainInputTo"];
|
||||
this.playVolume = this.config["play_volume"];
|
||||
this.minVolume = config["min_volume"] || -50.0;
|
||||
this.maxVolume = config["max_volume"] || -20.0;
|
||||
this.gapVolume = this.maxVolume - this.minVolume;
|
||||
}
|
||||
|
||||
YamahaAVRAccessory.prototype = {
|
||||
@@ -66,104 +143,74 @@ YamahaAVRAccessory.prototype = {
|
||||
|
||||
if (playing) {
|
||||
|
||||
yamaha.powerOn().then(function(){
|
||||
return yamaha.powerOn().then(function(){
|
||||
if (that.playVolume) return yamaha.setVolumeTo(that.playVolume*10);
|
||||
else return { then: function(f, r){ f(); } };
|
||||
else return Q();
|
||||
}).then(function(){
|
||||
if (that.setMainInputTo) return yamaha.setMainInputTo(that.setMainInputTo);
|
||||
else return { then: function(f, r){ f(); } };
|
||||
else return Q();
|
||||
}).then(function(){
|
||||
if (that.setMainInputTo == "AirPlay") return yamaha.SendXMLToReceiver(
|
||||
'<YAMAHA_AV cmd="PUT"><AirPlay><Play_Control><Playback>Play</Playback></Play_Control></AirPlay></YAMAHA_AV>'
|
||||
);
|
||||
else return { then: function(f, r){ f(); } };
|
||||
//else return Promise.fulfilled(undefined);
|
||||
else return Q();
|
||||
});
|
||||
}
|
||||
else {
|
||||
yamaha.powerOff();
|
||||
return yamaha.powerOff();
|
||||
}
|
||||
},
|
||||
|
||||
getServices: function() {
|
||||
var that = this;
|
||||
return [{
|
||||
sType: types.ACCESSORY_INFORMATION_STYPE,
|
||||
characteristics: [{
|
||||
cType: types.NAME_CTYPE,
|
||||
onUpdate: null,
|
||||
perms: ["pr"],
|
||||
format: "string",
|
||||
initialValue: this.name,
|
||||
supportEvents: false,
|
||||
supportBonjour: false,
|
||||
manfDescription: "Name of the accessory",
|
||||
designedMaxLength: 255
|
||||
},{
|
||||
cType: types.MANUFACTURER_CTYPE,
|
||||
onUpdate: null,
|
||||
perms: ["pr"],
|
||||
format: "string",
|
||||
initialValue: "Yamaha",
|
||||
supportEvents: false,
|
||||
supportBonjour: false,
|
||||
manfDescription: "Manufacturer",
|
||||
designedMaxLength: 255
|
||||
},{
|
||||
cType: types.MODEL_CTYPE,
|
||||
onUpdate: null,
|
||||
perms: ["pr"],
|
||||
format: "string",
|
||||
initialValue: this.sysConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0],
|
||||
supportEvents: false,
|
||||
supportBonjour: false,
|
||||
manfDescription: "Model",
|
||||
designedMaxLength: 255
|
||||
},{
|
||||
cType: types.SERIAL_NUMBER_CTYPE,
|
||||
onUpdate: null,
|
||||
perms: ["pr"],
|
||||
format: "string",
|
||||
initialValue: this.sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0],
|
||||
supportEvents: false,
|
||||
supportBonjour: false,
|
||||
manfDescription: "SN",
|
||||
designedMaxLength: 255
|
||||
},{
|
||||
cType: types.IDENTIFY_CTYPE,
|
||||
onUpdate: null,
|
||||
perms: ["pw"],
|
||||
format: "bool",
|
||||
initialValue: false,
|
||||
supportEvents: false,
|
||||
supportBonjour: false,
|
||||
manfDescription: "Identify Accessory",
|
||||
designedMaxLength: 1
|
||||
}]
|
||||
},{
|
||||
sType: types.SWITCH_STYPE,
|
||||
characteristics: [{
|
||||
cType: types.NAME_CTYPE,
|
||||
onUpdate: null,
|
||||
perms: ["pr"],
|
||||
format: "string",
|
||||
initialValue: this.serviceName,
|
||||
supportEvents: false,
|
||||
supportBonjour: false,
|
||||
manfDescription: "Name of service",
|
||||
designedMaxLength: 255
|
||||
},{
|
||||
cType: types.POWER_STATE_CTYPE,
|
||||
onUpdate: function(value) { that.setPlaying(value); },
|
||||
perms: ["pw","pr","ev"],
|
||||
format: "bool",
|
||||
initialValue: false,
|
||||
supportEvents: false,
|
||||
supportBonjour: false,
|
||||
manfDescription: "Change the playback state of the Yamaha AV Receiver",
|
||||
designedMaxLength: 1
|
||||
}]
|
||||
}];
|
||||
var informationService = new Service.AccessoryInformation();
|
||||
var yamaha = this.yamaha;
|
||||
|
||||
informationService
|
||||
.setCharacteristic(Characteristic.Name, this.name)
|
||||
.setCharacteristic(Characteristic.Manufacturer, "Yamaha")
|
||||
.setCharacteristic(Characteristic.Model, this.sysConfig.YAMAHA_AV.System[0].Config[0].Model_Name[0])
|
||||
.setCharacteristic(Characteristic.SerialNumber, this.sysConfig.YAMAHA_AV.System[0].Config[0].System_ID[0]);
|
||||
|
||||
var switchService = new Service.Switch("Power State");
|
||||
switchService.getCharacteristic(Characteristic.On)
|
||||
.on('get', function(callback, context){
|
||||
yamaha.isOn().then(function(result){
|
||||
callback(false, result);
|
||||
}.bind(this));
|
||||
}.bind(this))
|
||||
.on('set', function(powerOn, callback){
|
||||
this.setPlaying(powerOn).then(function(){
|
||||
callback(false, powerOn);
|
||||
}, function(error){
|
||||
callback(error, !powerOn); //TODO: Actually determine and send real new status.
|
||||
});
|
||||
}.bind(this));
|
||||
|
||||
var audioDeviceService = new YamahaAVRPlatform.AudioDeviceService("Audio Functions");
|
||||
audioDeviceService.getCharacteristic(YamahaAVRPlatform.AudioVolume)
|
||||
.on('get', function(callback, context){
|
||||
yamaha.getBasicInfo().done(function(basicInfo){
|
||||
var v = basicInfo.getVolume()/10.0;
|
||||
var p = 100 * ((v - that.minVolume) / that.gapVolume);
|
||||
p = p < 0 ? 0 : p > 100 ? 100 : Math.round(p);
|
||||
debug("Got volume percent of " + p + "%");
|
||||
callback(false, p);
|
||||
});
|
||||
})
|
||||
.on('set', function(p, callback){
|
||||
var v = ((p / 100) * that.gapVolume) + that.minVolume;
|
||||
v = Math.round(v*10.0);
|
||||
debug("Setting volume to " + v);
|
||||
yamaha.setVolumeTo(v).then(function(){
|
||||
callback(false, p);
|
||||
});
|
||||
})
|
||||
.getValue(null, null); // force an asynchronous get
|
||||
|
||||
|
||||
return [informationService, switchService, audioDeviceService];
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -89,11 +89,11 @@ ZWayServerPlatform.prototype = {
|
||||
//TODO: Unify this with getVDevServices, so there's only one place with mapping between service and vDev type.
|
||||
//Note: Order matters!
|
||||
var primaryDeviceClasses = [
|
||||
"switchBinary",
|
||||
"thermostat",
|
||||
"sensorBinary.Door/Window",
|
||||
"sensorMultilevel.Temperature",
|
||||
"switchMultilevel"
|
||||
"switchMultilevel",
|
||||
"switchBinary",
|
||||
"sensorBinary.Door/Window"
|
||||
];
|
||||
|
||||
var that = this;
|
||||
@@ -226,24 +226,24 @@ ZWayServerAccessory.prototype = {
|
||||
var typeKey = ZWayServerPlatform.getVDevTypeKey(vdev);
|
||||
var services = [], service;
|
||||
switch (typeKey) {
|
||||
case "switchBinary":
|
||||
services.push(new Service.Switch(vdev.metrics.title));
|
||||
break;
|
||||
case "switchMultilevel":
|
||||
services.push(new Service.Lightbulb(vdev.metrics.title));
|
||||
break;
|
||||
case "thermostat":
|
||||
case "thermostat":
|
||||
services.push(new Service.Thermostat(vdev.metrics.title));
|
||||
break;
|
||||
case "sensorMultilevel.Temperature":
|
||||
services.push(new Service.TemperatureSensor(vdev.metrics.title));
|
||||
break;
|
||||
case "sensorBinary.Door/Window":
|
||||
services.push(new Service.GarageDoorOpener(vdev.metrics.title));
|
||||
case "switchMultilevel":
|
||||
services.push(new Service.Lightbulb(vdev.metrics.title));
|
||||
break;
|
||||
case "battery.Battery":
|
||||
services.push(new Service.BatteryService(vdev.metrics.title));
|
||||
break;
|
||||
case "switchBinary":
|
||||
services.push(new Service.Switch(vdev.metrics.title));
|
||||
break;
|
||||
case "sensorBinary.Door/Window":
|
||||
services.push(new Service.GarageDoorOpener(vdev.metrics.title));
|
||||
break;
|
||||
case "sensorMultilevel.Luminiscence":
|
||||
services.push(new Service.LightSensor(vdev.metrics.title));
|
||||
break;
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
{
|
||||
"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",
|
||||
"Target": {
|
||||
"Set": "1/2/3",
|
||||
"Listen": "1/2/4"
|
||||
},
|
||||
"Current": {
|
||||
"Set": "1/3/1",
|
||||
"Listen": "1/3/2"
|
||||
},
|
||||
"PositionState": {
|
||||
"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"
|
||||
}
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
],
|
||||
"accessories": []
|
||||
}
|
||||
Reference in New Issue
Block a user