mirror of
https://github.com/mtan93/homebridge.git
synced 2026-03-08 05:31:55 +00:00
supports switches, lightbulbs (dimmers and rgb/colored lights), homematic window blinds, max and homematic thermostats, homematic door&window sensors, PRESENCE and sonos (including volume) the shim is bidirectional and uses longpoll to push fhem events back to homekit. fhem events are cached so homekit querys for known values will not cause a roundtrip to fhem more: http://forum.fhem.de/index.php/topic,32652.msg314166.html#msg314166
1322 lines
42 KiB
JavaScript
1322 lines
42 KiB
JavaScript
// FHEM Platform Shim for HomeBridge
|
||
//
|
||
// Remember to add platform to config.json. Example:
|
||
// "platforms": [
|
||
// {
|
||
// "platform": "FHEM",
|
||
// "name": "FHEM",
|
||
// "server": "127.0.0.1",
|
||
// "port": "8083",
|
||
// "ssl": "true",
|
||
// "auth": {"user": "fhem", "pass": "fhempassword"},
|
||
// "filter": "room=xyz"
|
||
// }
|
||
// ],
|
||
//
|
||
// 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 util = require('util');
|
||
|
||
|
||
// cached readings from longpoll & query
|
||
var FHEM_cached = {};
|
||
|
||
|
||
// subscriptions to fhem longpoll evens
|
||
var FHEM_subscriptions = {};
|
||
function
|
||
FHEM_subscribe(characteristic, inform_id, accessory) {
|
||
FHEM_subscriptions[inform_id] = { 'characteristic': characteristic, 'accessory': accessory };
|
||
//FHEM_subscriptions[inform_id] = characteristic;
|
||
}
|
||
function
|
||
FHEM_update(inform_id, value, no_update) {
|
||
var subscription = FHEM_subscriptions[inform_id];
|
||
if( subscription != undefined ) {
|
||
FHEM_cached[inform_id] = value;
|
||
console.log(" caching: " + inform_id + ": " + value + " as " + typeof(value) );
|
||
|
||
if( !no_update )
|
||
subscription.characteristic.updateValue(value, null);
|
||
}
|
||
}
|
||
|
||
|
||
var FHEM_longpoll_running = false;
|
||
//FIXME: add reconnect, force reconnect on xxx bytes received, add filter, add since
|
||
function FHEM_startLongpoll(connection) {
|
||
if( FHEM_longpoll_running )
|
||
return;
|
||
FHEM_longpoll_running = true;
|
||
|
||
var filter = ".*";
|
||
var since = "null";
|
||
var query = "/fhem.pl?XHR=1"+
|
||
"&inform=type=status;filter="+filter+";since="+since+";fmt=JSON"+
|
||
"×tamp="+new Date().getTime();
|
||
|
||
var url = encodeURI( connection.base_url + query );
|
||
console.log( 'starting longpoll: ' + url );
|
||
|
||
var FHEM_longpollOffset = 0;
|
||
var input = "";
|
||
connection.request.get( { url: url } ).on( 'data', function(data) {
|
||
//console.log( 'data: '+ data );
|
||
if( !data )
|
||
return;
|
||
|
||
input += data;
|
||
for(;;) {
|
||
var nOff = input.indexOf("\n", FHEM_longpollOffset);
|
||
if(nOff < 0)
|
||
break;
|
||
var l = input.substr(FHEM_longpollOffset, nOff-FHEM_longpollOffset);
|
||
FHEM_longpollOffset = nOff+1;
|
||
//console.log( "Rcvd: "+(l.length>132 ? l.substring(0,132)+"...("+l.length+")":l) );
|
||
if(!l.length)
|
||
continue;
|
||
|
||
var d;
|
||
if( l.substr(0,1) == '[' )
|
||
d = JSON.parse(l);
|
||
else
|
||
d = l.split("<<", 3);
|
||
|
||
//console.log(d);
|
||
|
||
if(d.length != 3)
|
||
continue;
|
||
if(d[0].match(/-ts$/))
|
||
continue;
|
||
|
||
//console.log( "Rcvd: "+(l.length>132 ? l.substring(0,132)+"...("+l.length+")":l) );
|
||
|
||
//FIXME: create reading2value
|
||
//FIXME: redirect device reading to homekit reading: rgb->hue,...
|
||
var subscription = FHEM_subscriptions[d[0]];
|
||
if( subscription != undefined ) {
|
||
//console.log( "Rcvd: "+(l.length>132 ? l.substring(0,132)+"...("+l.length+")":l) );
|
||
var accessory = subscription.accessory;
|
||
var value = d[1];
|
||
|
||
if(d[0].match(/-state$/)) {
|
||
if( match = d[1].match(/dim(\d*)%/ ) ) {
|
||
var pct = parseInt( match[1] );
|
||
|
||
FHEM_update( d[0].replace( '-state', '-pct' ), pct );
|
||
}
|
||
|
||
value = 1;
|
||
if( d[1] == 'off' )
|
||
value = 0;
|
||
else if( d[1] == '000000' )
|
||
value = 0;
|
||
else if( d[1] == 'absent' )
|
||
value = 0;
|
||
else if( d[1] == 'A0' )
|
||
value = 0;
|
||
|
||
value = parseInt(value);
|
||
|
||
} else if(d[0].match(/-motor$/)) {
|
||
value = 2;
|
||
if( d[1].match(/^opening/))
|
||
value = 1;
|
||
else if( d[1].match(/^up/))
|
||
value = 1;
|
||
else if( d[1].match(/^closing/))
|
||
value = 0;
|
||
else if( d[1].match(/^down/))
|
||
value = 0;
|
||
|
||
value = parseInt(value);
|
||
|
||
} else if(d[0].match(/-transportState$/)) {
|
||
value = 0;
|
||
if( d[1] == 'PLAYING' )
|
||
value = 1;
|
||
|
||
value = parseInt(value);
|
||
|
||
} else if(d[0].match(/-Volume$/)) {
|
||
value = parseInt( d[1] );
|
||
|
||
} else if(d[0].match(/-contact$/)) {
|
||
value = 0;
|
||
if( d[1].match( /^closed/ ) )
|
||
value = 1;
|
||
|
||
value = parseInt(value);
|
||
|
||
} else if(d[0].match(/-pct$/)) {
|
||
value = parseInt( d[1] );
|
||
|
||
} else if(d[0].match(/-hue$/)) {
|
||
value = Math.round(d[1] * 360 / accessory.hueMax);
|
||
|
||
} else if(d[0].match(/-sat$/)) {
|
||
result = Math.round(d[1] * 100 / accessory.satMax);
|
||
|
||
} else if(d[0].match(/-rgb$/)) {
|
||
var hue = parseInt( FHEM_rgb2h(d[1]) * 360 );
|
||
|
||
FHEM_update( d[0].replace( '-rgb', '-hue' ), hue );
|
||
|
||
} else if(d[0].match(/-temperature$/)
|
||
|| d[0].match(/-measured-temp$/)
|
||
|| d[0].match(/-desired-temp$/)
|
||
|| d[0].match(/-desiredTemperature$/) ) {
|
||
value = parseFloat( d[1] );
|
||
|
||
} else if(d[0].match(/-humidity$/)) {
|
||
value = parseInt( d[1] );
|
||
|
||
}
|
||
|
||
FHEM_update( d[0], value );
|
||
}
|
||
|
||
}
|
||
|
||
input = input.substr(FHEM_longpollOffset);
|
||
FHEM_longpollOffset = 0;
|
||
|
||
} ).on( 'error', function(err) {
|
||
console.log( "longpoll error: " + err );
|
||
|
||
FHEM_longpoll_running = false;
|
||
setTimeout( function(){FHEM_startLongpoll(connection)}, 5000 );
|
||
} );
|
||
}
|
||
|
||
|
||
function FHEMPlatform(log, config) {
|
||
this.log = log;
|
||
this.server = config['server'];
|
||
this.port = config['port'];
|
||
this.filter = config['filter'];
|
||
|
||
var base_url;
|
||
if( config['ssl'] )
|
||
base_url = 'https://';
|
||
else
|
||
base_url = 'http://';
|
||
base_url += this.server + ':' + this.port;
|
||
|
||
var request = require('request');
|
||
var auth = config['auth'];
|
||
if( auth ) {
|
||
if( auth.sendImmediately == undefined )
|
||
auth.sendImmediately = false;
|
||
console.log( "auth: "+ auth );
|
||
|
||
request = request.defaults( { 'auth': auth, 'rejectUnauthorized': false } );
|
||
}
|
||
|
||
this.connection = { 'base_url': base_url, 'request': request };
|
||
|
||
FHEM_startLongpoll( this.connection );
|
||
}
|
||
|
||
function
|
||
FHEM_sortByKey(array, key) {
|
||
return array.sort( function(a, b) {
|
||
var x = a[key]; var y = b[key];
|
||
return ((x < y) ? -1 : ((x > y) ? 1 : 0));
|
||
});
|
||
}
|
||
|
||
function
|
||
FHEM_rgb2hex(r,g,b) {
|
||
if( g == undefined )
|
||
return Number(0x1000000 + r[0]*0x10000 + r[1]*0x100 + r[2]).toString(16).substring(1);
|
||
|
||
return Number(0x1000000 + r*0x10000 + g*0x100 + b).toString(16).substring(1);
|
||
}
|
||
|
||
function
|
||
FHEM_hsv2rgb(h,s,v) {
|
||
var r = 0.0;
|
||
var g = 0.0;
|
||
var b = 0.0;
|
||
|
||
if( s == 0 ) {
|
||
r = v;
|
||
g = v;
|
||
b = v;
|
||
|
||
} else {
|
||
var i = Math.floor( h * 6.0 );
|
||
var f = ( h * 6.0 ) - i;
|
||
var p = v * ( 1.0 - s );
|
||
var q = v * ( 1.0 - s * f );
|
||
var t = v * ( 1.0 - s * ( 1.0 - f ) );
|
||
i = i % 6;
|
||
|
||
if( i == 0 ) {
|
||
r = v;
|
||
g = t;
|
||
b = p;
|
||
} else if( i == 1 ) {
|
||
r = q;
|
||
g = v;
|
||
b = p;
|
||
} else if( i == 2 ) {
|
||
r = p;
|
||
g = v;
|
||
b = t;
|
||
} else if( i == 3 ) {
|
||
r = p;
|
||
g = q;
|
||
b = v;
|
||
} else if( i == 4 ) {
|
||
r = t;
|
||
g = p;
|
||
b = v;
|
||
} else if( i == 5 ) {
|
||
r = v;
|
||
g = p;
|
||
b = q;
|
||
}
|
||
}
|
||
|
||
return FHEM_rgb2hex( Math.round(r*255),Math.round(g*255),Math.round(b*255) );
|
||
}
|
||
|
||
function
|
||
FHEM_rgb2h(r,g,b){
|
||
if( r == undefined )
|
||
return;
|
||
|
||
if( g == undefined ) {
|
||
var str = r;
|
||
r = parseInt( str.substr(0,2), 16 );
|
||
g = parseInt( str.substr(2,2), 16 );
|
||
b = parseInt( str.substr(4,2), 16 );
|
||
}
|
||
|
||
var M = Math.max( r, g, b );
|
||
var m = Math.min( r, g, b );
|
||
var c = M - m;
|
||
|
||
var h, s, v;
|
||
if( c == 0 ) {
|
||
h = 0;
|
||
} else if( M == r ) {
|
||
h = ( 60 * ( ( g - b ) / c ) % 360 ) / 360;
|
||
} else if( M == g ) {
|
||
h = ( 60 * ( ( b - r ) / c ) + 120 ) / 360;
|
||
} else if( M == b ) {
|
||
h = ( 60 * ( ( r - g ) / c ) + 240 ) / 360;
|
||
}
|
||
|
||
return h;
|
||
|
||
if( M == 0 ) {
|
||
s = 0;
|
||
} else {
|
||
s = c / M;
|
||
}
|
||
|
||
v = M;
|
||
|
||
return h;
|
||
}
|
||
|
||
|
||
FHEMPlatform.prototype = {
|
||
accessories: function(callback) {
|
||
this.log("Fetching FHEM switchable devices...");
|
||
|
||
var foundAccessories = [];
|
||
|
||
var cmd = 'jsonlist2';
|
||
if( this.filter )
|
||
cmd += " " + this.filter;
|
||
var url = encodeURI( this.connection.base_url + "/fhem?cmd=" + cmd + "&XHR=1");
|
||
this.log( 'fetching: ' + url );
|
||
|
||
var that = this;
|
||
this.connection.request.get( { url: url, json: true, gzip: true },
|
||
function(err, response, json) {
|
||
if( !err && response.statusCode == 200 ) {
|
||
that.log( 'got: ' + json['totalResultsReturned'] + ' results' );
|
||
//that.log("got json: " + util.inspect(json) );
|
||
if( json['totalResultsReturned'] ) {
|
||
var sArray=FHEM_sortByKey(json['Results'],"Name");
|
||
sArray.map(function(s) {
|
||
if( s.Attributes.disable == 1 ) {
|
||
that.log( s.Internals.NAME + ' is disabled');
|
||
|
||
} else if( s.Internals.TYPE == 'structure' ) {
|
||
that.log( s.Internals.NAME + ' is a structure');
|
||
|
||
} else if( s.PossibleSets.match(/\bon\b/)
|
||
&& s.PossibleSets.match(/\boff\b/) ) {
|
||
accessory = new FHEMAccessory(that.log, that.connection, s);
|
||
foundAccessories.push(accessory);
|
||
|
||
} else if( s.PossibleSets.match(/\bvolume\b/) ) {
|
||
that.log( s.Internals.NAME + ' has volume');
|
||
accessory = new FHEMAccessory(that.log, that.connection, s);
|
||
foundAccessories.push(accessory);
|
||
|
||
} else if( s.Attributes.genericDisplayType
|
||
|| s.Attributes.genericDeviceType ) {
|
||
accessory = new FHEMAccessory(that.log, that.connection, s);
|
||
foundAccessories.push(accessory);
|
||
|
||
} else if( s.Attributes.subType == 'thermostat'
|
||
|| s.Attributes.subType == 'blindActuator'
|
||
|| s.Attributes.subType == 'threeStateSensor' ) {
|
||
accessory = new FHEMAccessory(that.log, that.connection, s);
|
||
foundAccessories.push(accessory);
|
||
|
||
} else if( s.Internals.TYPE == 'PRESENCE' ) {
|
||
accessory = new FHEMAccessory(that.log, that.connection, s);
|
||
foundAccessories.push(accessory);
|
||
|
||
|
||
} else if( s.Readings.temperature ) {
|
||
accessory = new FHEMAccessory(that.log, that.connection, s);
|
||
foundAccessories.push(accessory);
|
||
|
||
} else if( s.Readings.humidity ) {
|
||
accessory = new FHEMAccessory(that.log, that.connection, s);
|
||
foundAccessories.push(accessory);
|
||
|
||
} else {
|
||
that.log( 'ignoring ' + s.Internals.NAME );
|
||
|
||
}
|
||
});
|
||
}
|
||
callback(foundAccessories);
|
||
|
||
} else {
|
||
that.log("There was a problem connecting to FHEM (1).");
|
||
if( response )
|
||
that.log( " " + response.statusCode + ": " + response.statusMessage );
|
||
|
||
}
|
||
|
||
});
|
||
}
|
||
}
|
||
|
||
function
|
||
FHEMAccessory(log, connection, s) {
|
||
//log( 'sets: ' + s.PossibleSets );
|
||
//log("got json: " + util.inspect(s) );
|
||
//log("got json: " + util.inspect(s.Internals) );
|
||
|
||
//FIXME: replace hasPct(true/false) by hasBri(reading)
|
||
var match;
|
||
if( match = s.PossibleSets.match(/\bpct\b/) ) {
|
||
s.hasPct = true;
|
||
s.pctMax = 100;
|
||
} else if( match = s.PossibleSets.match(/\bdim\d*%/) ) {
|
||
s.hasDim = true;
|
||
s.pctMax = 100;
|
||
}
|
||
if( match = s.PossibleSets.match(/\bhue[^\b\s]*(,(\d*)?)+\b/) ) {
|
||
s.isLight = true;
|
||
s.hasHue = true;
|
||
s.hueMax = 360;
|
||
if( match[2] != undefined )
|
||
s.hueMax = match[2];
|
||
}
|
||
if( match = s.PossibleSets.match(/\bsat[^\b\s]*(,(\d*)?)+\b/) ) {
|
||
s.isLight = true;
|
||
s.hasSat = true;
|
||
s.satMax = 100;
|
||
if( match[2] != undefined )
|
||
s.satMax = match[2];
|
||
}
|
||
if( s.PossibleSets.match(/\brgb\b/) ) {
|
||
s.isLight = true;
|
||
s.hasRGB = true;
|
||
}
|
||
|
||
if( s.Readings['measured-temp'] )
|
||
s.hasTemperature = "measured-temp";
|
||
else if( s.Readings.temperature )
|
||
s.hasTemperature = "temperature";
|
||
|
||
if( s.Readings.humidity )
|
||
s.hasHumidity = true;
|
||
|
||
if( s.Readings.motor )
|
||
s.hasMotor = 'motor';
|
||
|
||
|
||
var genericType = s.Attributes.genericDeviceType;
|
||
if( !genericType )
|
||
genericType = s.Attributes.genericDisplayType;
|
||
|
||
if( genericType == 'light' )
|
||
s.isLight = true;
|
||
else if( genericType == 'blind' ) {
|
||
s.hasPct = false;
|
||
s.isBlind = 'pct';
|
||
} else if( genericType == 'thermostat' )
|
||
s.isThermostat = true;
|
||
else if( s.Attributes.subType == 'thermostat' )
|
||
s.isThermostat = true;
|
||
else if( s.Attributes.subType == 'blindActuator' ) {
|
||
s.hasPct = false;
|
||
s.isBlind = 'pct';
|
||
} else if( s.Attributes.subType == 'threeStateSensor' ) {
|
||
s.isContactSensor = true;
|
||
if( s.Attributes.model == 'HM-SEC-RHS' )
|
||
s.isWindow = true;
|
||
} else if( s.Internals.TYPE == 'PRESENCE' )
|
||
s.isOccupancySensor = true;
|
||
//else if( s.PossibleSets.match(/\bon\b/)
|
||
// && s.PossibleSets.match(/\boff\b/) )
|
||
// s.isSwitch = true;
|
||
|
||
if( s.PossibleSets.match(/\bdesired-temp\b/) )
|
||
s.isThermostat = 'desired-temp';
|
||
else if( s.PossibleSets.match(/\bdesiredTemperature\b/) )
|
||
s.isThermostat = 'desiredTemperature';
|
||
|
||
if( s.hasHue )
|
||
log( s.Internals.NAME + ' has hue [0-' + s.hueMax +']' );
|
||
else if( s.hasRGB )
|
||
log( s.Internals.NAME + ' has RGB');
|
||
else if( s.hasPct )
|
||
log( s.Internals.NAME + ' is dimable [0-'+ s.pctMax +']' );
|
||
else if( s.hasDim )
|
||
log( s.Internals.NAME + ' is dimable [0-'+ s.pctMax +']' );
|
||
else if( s.isThermostat )
|
||
log( s.Internals.NAME + ' is thermostat ['+ s.isThermostat +']' );
|
||
else if( s.isContactSensor )
|
||
log( s.Internals.NAME + ' is contactsensor' );
|
||
else if( s.isOccupancySensor )
|
||
log( s.Internals.NAME + ' is occupancysensor' );
|
||
else if( s.isBlind )
|
||
log( s.Internals.NAME + ' is blind ['+ s.isBlind +']' );
|
||
else if( s.isLight )
|
||
log( s.Internals.NAME + ' is light' );
|
||
else
|
||
log( s.Internals.NAME + ' is switchable' );
|
||
|
||
if( s.hasTemperature )
|
||
log( s.Internals.NAME + ' has temperature ['+ s.hasTemperature +']' );
|
||
if( s.hasHumidity )
|
||
log( s.Internals.NAME + ' has humidity' );
|
||
if( s.hasMotor )
|
||
log( s.Internals.NAME + ' has motor' );
|
||
|
||
//FIXME: create redirectReading() / redirectSet(): on/off -> play/pause
|
||
|
||
// device info
|
||
this.name = s.Internals.NAME;
|
||
this.alias = s.Attributes.alias ? s.Attributes.alias : s.Internals.NAME;
|
||
this.device = s.Internals.NAME;
|
||
this.type = s.Internals.TYPE;
|
||
this.model = s.Attributes.model ? s.Attributes.model : s.Internals.model;
|
||
this.PossibleSets = s.PossibleSets;
|
||
|
||
if( this.type == 'CUL_HM' ) {
|
||
this.serial = s.Internals.DEF;
|
||
if( s.Attributes.serialNr )
|
||
this.serial = s.Attributes.serialNr;
|
||
} else if( this.type == 'CUL_WS' )
|
||
this.serial = s.Internals.DEF;
|
||
else if( this.type == 'FS20' )
|
||
this.serial = s.Internals.DEF;
|
||
else if( this.type == 'IT' )
|
||
this.serial = s.Internals.DEF;
|
||
else if( this.type == 'HUEDevice' )
|
||
this.serial = s.Internals.uniqueid;
|
||
else if( this.type == 'SONOSPLAYER' )
|
||
this.serial = s.Internals.UDN;
|
||
|
||
this.hasPct = s.hasPct;
|
||
this.hasDim = s.hasDim;
|
||
this.pctMax = s.pctMax;
|
||
this.hasHue = s.hasHue;
|
||
this.hueMax = s.hueMax;
|
||
this.hasSat = s.hasSat;
|
||
this.satMax = s.satMax;
|
||
this.hasRGB = s.hasRGB;
|
||
|
||
this.hasTemperature = s.hasTemperature;
|
||
this.hasHumidity = s.hasHumidity;
|
||
this.hasMotor = s.hasMotor;
|
||
|
||
this.isLight = s.isLight;
|
||
this.isBlind = s.isBlind;
|
||
this.isThermostat = s.isThermostat;
|
||
this.isContactSensor = s.isContactSensor;
|
||
this.isOccupancySensor = s.isOccupancySensor;
|
||
this.isWindow = s.isWindow;
|
||
|
||
//log( util.inspect(s.Readings) );
|
||
|
||
this.log = log;
|
||
this.connection = connection;
|
||
}
|
||
|
||
FHEM_dim_values = [ 'dim06%', 'dim12%', 'dim18%', 'dim25%', 'dim31%', 'dim37%', 'dim43%', 'dim50%', 'dim56%', 'dim62%', 'dim68%', 'dim75%', 'dim81%', 'dim87%', 'dim93%' ];
|
||
|
||
FHEMAccessory.prototype = {
|
||
delayed: function(c,value,delay) {
|
||
var timer = this.delayed[c];
|
||
if( timer ) {
|
||
//this.log(this.name + " removing old command " + c);
|
||
clearTimeout( timer );
|
||
}
|
||
|
||
this.log(this.name + " delaying command " + c + " with value " + value);
|
||
var that = this;
|
||
this.delayed[c] = setTimeout( function(){clearTimeout(that.delayed[c]);that.command(c,value)}, delay?delay:1000 );
|
||
},
|
||
|
||
command: function(c,value) {
|
||
this.log(this.name + " sending command " + c + " with value " + value);
|
||
if( c == 'on' ) {
|
||
if( this.PossibleSets.match(/\bplay\b/i) )
|
||
url = this.connection.base_url + "/fhem?cmd=set " + this.device + " play&XHR=1";
|
||
else if( this.PossibleSets.match(/\bon\b/) )
|
||
url = this.connection.base_url + "/fhem?cmd=set " + this.device + " on&XHR=1";
|
||
else
|
||
this.log(this.name + " Unhandled command! cmd=" + c + ", value=" + value);
|
||
|
||
} else if( c == 'off' ) {
|
||
if( this.PossibleSets.match(/\bpause\b/i) )
|
||
url = this.connection.base_url + "/fhem?cmd=set " + this.device + " pause&XHR=1";
|
||
else if( this.PossibleSets.match(/\boff\b/) )
|
||
url = this.connection.base_url + "/fhem?cmd=set " + this.device + " off&XHR=1";
|
||
else
|
||
this.log(this.device + " Unhandled command! cmd=" + c + ", value=" + value);
|
||
|
||
} else if( c == 'volume' ) {
|
||
url = this.connection.base_url + "/fhem?cmd=set " + this.device + " volume " + value + "&XHR=1";
|
||
|
||
} else if( c == 'pct' ) {
|
||
url = this.connection.base_url + "/fhem?cmd=set " + this.device + " pct " + value + "&XHR=1";
|
||
|
||
} else if( c == 'dim' ) {
|
||
//if( value < 3 )
|
||
// url = this.connection.base_url + "/fhem?cmd=set " + this.device + " off&XHR=1";
|
||
//else
|
||
if( value > 97 )
|
||
url = this.connection.base_url + "/fhem?cmd=set " + this.device + " on&XHR=1";
|
||
else
|
||
url = this.connection.base_url + "/fhem?cmd=set " + this.device + " " + FHEM_dim_values[Math.round(value/6.25)] + "&XHR=1";
|
||
|
||
} else if( c == 'hue' ) {
|
||
if( !this.hasHue ) {
|
||
value = FHEM_hsv2rgb( value/360.0, this.sat?this.sat/100.0:1.0, this.pct?this.pct/100.0:1.0 );
|
||
url = this.connection.base_url + "/fhem?cmd=set " + this.device + " rgb " + value + "&XHR=1";
|
||
|
||
} else {
|
||
value = Math.round(value * this.hueMax / 360);
|
||
url = this.connection.base_url + "/fhem?cmd=set " + this.device + " hue " + value + "&XHR=1";
|
||
}
|
||
|
||
} else if( c == 'sat' ) {
|
||
value = value / 100 * this.satMax;
|
||
url = this.connection.base_url + "/fhem?cmd=set " + this.device + " sat " + value + "&XHR=1";
|
||
|
||
} else if( c == 'targetTemperature' ) {
|
||
url = this.connection.base_url + "/fhem?cmd=set " + this.device + " " + this.isThermostat + " " + value + "&XHR=1";
|
||
|
||
} else if( c == 'targetPosition' ) {
|
||
url = this.connection.base_url + "/fhem?cmd=set " + this.device + " " + this.isBlind + " " + value + "&XHR=1";
|
||
|
||
} else if( value != undefined ) {
|
||
this.log(this.name + " Unhandled command! cmd=" + c + ", value=" + value);
|
||
return;
|
||
|
||
}
|
||
|
||
var that = this;
|
||
this.connection.request.put( { url: encodeURI(url), gzip: true },
|
||
function(err, response) {
|
||
if( err ) {
|
||
that.log("There was a problem sending command " + c + " to" + that.name);
|
||
that.log(url);
|
||
if( response )
|
||
that.log( " " + response.statusCode + ": " + response.statusMessage );
|
||
|
||
} else {
|
||
that.log(that.name + " sent command " + c);
|
||
that.log(url);
|
||
|
||
}
|
||
|
||
} );
|
||
},
|
||
|
||
query: function(reading, callback) {
|
||
this.log("query: " + reading);
|
||
|
||
var orig_reading = reading;
|
||
|
||
//FIXME: create redirectReading()
|
||
var rgb_to_hue = false;
|
||
if( reading == 'hue' && !this.hasHue && this.hasRGB ) {
|
||
reading = 'rgb';
|
||
rgb_to_hue = true;
|
||
|
||
} else if( reading == 'pct' && !this.hasPct ) {
|
||
reading = 'state';
|
||
|
||
}
|
||
|
||
if( reading == 'rgb'
|
||
&& this.type == 'SWAP_0000002200000003' ) {
|
||
reading = '0B-RGBlevel';
|
||
|
||
}
|
||
|
||
var result = FHEM_cached[this.device + '-' + orig_reading];
|
||
if( result != undefined ) {
|
||
this.log(" cached: " + result);
|
||
if( callback != undefined )
|
||
callback(result);
|
||
return(result);
|
||
} else
|
||
this.log(" not cached" );
|
||
|
||
var cmd = '{ReadingsVal("'+this.device+'","'+reading+'","")}';
|
||
var url = encodeURI( this.connection.base_url + "/fhem?cmd=" + cmd + "&XHR=1");
|
||
this.log( ' querying: ' + url );
|
||
|
||
var that = this;
|
||
that.connection.request.get( { url: url, gzip: true },
|
||
function(err, response, result) {
|
||
if( !err && response.statusCode == 200 ) {
|
||
result = result.replace(/[\r\n]/g, "");
|
||
that.log(" result: " + result);
|
||
|
||
//FIXME: create reading2value
|
||
if( rgb_to_hue ) {
|
||
result = parseInt( FHEM_rgb2h(result) * 360 );
|
||
|
||
} else if( reading == 'hue' ) {
|
||
result = Math.round(result * 360 / that.hueMax);
|
||
|
||
} else if( reading == 'sat' ) {
|
||
result = Math.round(result * 100 / that.satMax);
|
||
|
||
} else if( reading == 'pct' ) {
|
||
result = parseInt( result );
|
||
|
||
} else if(reading.match(/-motor$/)) {
|
||
if( result.match(/^opening/))
|
||
result = 1;
|
||
else if( result.match(/^up/))
|
||
result = 1;
|
||
else if( result.match(/^closing/))
|
||
result = 0;
|
||
else if( result.match(/^down/))
|
||
result = 0;
|
||
else
|
||
result = 2;
|
||
|
||
result = parseInt(result);
|
||
|
||
} else if( reading == 'transportState' ) {
|
||
if( result == 'PLAYING' )
|
||
result = 1;
|
||
else
|
||
result = 0;
|
||
|
||
result = parseInt(result);
|
||
|
||
} else if( reading == 'Volume' ) {
|
||
result = parseInt( result );
|
||
|
||
} else if( reading == 'contact' ) {
|
||
if( result.match( /^closed/ ) )
|
||
result = 1;
|
||
else
|
||
result = 0;
|
||
|
||
result = parseInt(result);
|
||
|
||
} else if( reading == 'temperature'
|
||
|| reading == 'measured-temp'
|
||
|| reading == 'desired-temp'
|
||
|| reading == 'desiredTemperature' ) {
|
||
result = parseFloat( result );
|
||
|
||
} else if( reading == 'humidity' ) {
|
||
result = parseInt( result );
|
||
|
||
} else if( reading == 'state' ) {
|
||
if( orig_reading == 'pct' ) {
|
||
if( match = result.match(/dim(\d*)%/ ) ) {
|
||
result = match[1];
|
||
} else if( result == 'off' )
|
||
result = 0;
|
||
else
|
||
result = 100;
|
||
|
||
} else if( result == 'off' )
|
||
result = 0;
|
||
else if( result == 'absent' )
|
||
result = 0;
|
||
else if( result == '000000' )
|
||
result = 0;
|
||
else if( result == 'A0' )
|
||
result = 0;
|
||
else
|
||
result = 1;
|
||
|
||
result = parseInt( result );
|
||
|
||
}
|
||
that.log(" mapped: " + result);
|
||
|
||
if( !rgb_to_hue && reading != 'transportState' && reading != '0B-RGBlevel' )
|
||
FHEM_update( that.device + '-' + orig_reading, result, true );
|
||
|
||
if( callback != undefined )
|
||
callback(result);
|
||
return(result);
|
||
|
||
} else {
|
||
that.log("There was a problem connecting to FHEM (2).");
|
||
if( response )
|
||
that.log( " " + response.statusCode + ": " + response.statusMessage );
|
||
|
||
}
|
||
|
||
} );
|
||
},
|
||
|
||
informationCharacteristics: function() {
|
||
return [
|
||
{
|
||
cType: types.NAME_CTYPE,
|
||
onUpdate: null,
|
||
perms: ["pr"],
|
||
format: "string",
|
||
initialValue: this.alias,
|
||
supportEvents: false,
|
||
supportBonjour: false,
|
||
manfDescription: "Name of the accessory",
|
||
designedMaxLength: 255
|
||
},{
|
||
cType: types.MANUFACTURER_CTYPE,
|
||
onUpdate: null,
|
||
perms: ["pr"],
|
||
format: "string",
|
||
initialValue: "FHEM:"+this.type,
|
||
supportEvents: false,
|
||
supportBonjour: false,
|
||
manfDescription: "Manufacturer",
|
||
designedMaxLength: 255
|
||
},{
|
||
cType: types.MODEL_CTYPE,
|
||
onUpdate: null,
|
||
perms: ["pr"],
|
||
format: "string",
|
||
initialValue: this.model ? this.model : '<unknown>',
|
||
supportEvents: false,
|
||
supportBonjour: false,
|
||
manfDescription: "Model",
|
||
designedMaxLength: 255
|
||
},{
|
||
cType: types.SERIAL_NUMBER_CTYPE,
|
||
onUpdate: null,
|
||
perms: ["pr"],
|
||
format: "string",
|
||
initialValue: this.serial ? this.serial : "A1S2NASF88EW",
|
||
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
|
||
}
|
||
]
|
||
},
|
||
|
||
controlCharacteristics: function(that) {
|
||
cTypes = [{
|
||
cType: types.NAME_CTYPE,
|
||
onUpdate: null,
|
||
perms: ["pr"],
|
||
format: "string",
|
||
initialValue: this.alias,
|
||
supportEvents: true,
|
||
supportBonjour: false,
|
||
manfDescription: "Name of service",
|
||
designedMaxLength: 255
|
||
}]
|
||
|
||
if( this.name != undefined
|
||
&& !this.hasTemperature
|
||
&& !this.hasHumidity
|
||
&& !this.isBlind
|
||
&& !this.isThermostat
|
||
&& !this.isContactSensor
|
||
&& !this.isOccupancySensor ) {
|
||
cTypes.push({
|
||
cType: types.POWER_STATE_CTYPE,
|
||
onRegister: function(characteristic) {
|
||
characteristic.eventEnabled = true;
|
||
if( that.type == 'SONOSPLAYER' )
|
||
FHEM_subscribe(characteristic, that.name+'-transportState', that);
|
||
else
|
||
FHEM_subscribe(characteristic, that.name+'-state', that);
|
||
},
|
||
onUpdate: function(value) {
|
||
that.command( value == 0 ? 'off' : 'on' );
|
||
},
|
||
onRead: function(callback) {
|
||
that.query( that.type == 'SONOSPLAYER' ? 'transportState' : 'state', function(state){ callback(state) } );
|
||
},
|
||
perms: ["pw","pr","ev"],
|
||
format: "bool",
|
||
initialValue: 0,
|
||
//initialValue: that.query( that.type == 'SONOSPLAYER' ? 'transportState' : 'state' ),
|
||
supportEvents: true,
|
||
supportBonjour: false,
|
||
manfDescription: "Change the power state",
|
||
designedMaxLength: 1
|
||
});
|
||
}
|
||
|
||
if( this.hasPct ) {
|
||
cTypes.push({
|
||
cType: types.BRIGHTNESS_CTYPE,
|
||
onRegister: function(characteristic) {
|
||
characteristic.eventEnabled = true;
|
||
FHEM_subscribe(characteristic, that.name+'-pct', that);
|
||
},
|
||
onUpdate: function(value) { that.command('pct', value); },
|
||
onRead: function(callback) {
|
||
that.query('pct', function(pct){
|
||
callback(pct);
|
||
});
|
||
},
|
||
perms: ["pw","pr","ev"],
|
||
format: "int",
|
||
initialValue: 0,
|
||
//initialValue: that.query( 'pct' ),
|
||
supportEvents: true,
|
||
supportBonjour: false,
|
||
manfDescription: "Adjust Brightness of the Light",
|
||
designedMinValue: 0,
|
||
designedMaxValue: this.pctMax,
|
||
designedMinStep: 1,
|
||
unit: "%"
|
||
});
|
||
} else if( this.hasDim ) {
|
||
cTypes.push({
|
||
cType: types.BRIGHTNESS_CTYPE,
|
||
onRegister: function(characteristic) {
|
||
characteristic.eventEnabled = true;
|
||
FHEM_subscribe(characteristic, that.name+'-pct', that);
|
||
},
|
||
onUpdate: function(value) { that.delayed('dim', value); },
|
||
onRead: function(callback) {
|
||
that.query('pct', function(pct){
|
||
callback(pct);
|
||
});
|
||
},
|
||
perms: ["pw","pr","ev"],
|
||
format: "int",
|
||
initialValue: 0,
|
||
//initialValue: that.query( 'state' ),
|
||
supportEvents: true,
|
||
supportBonjour: false,
|
||
manfDescription: "Adjust Brightness of the Light",
|
||
designedMinValue: 0,
|
||
designedMaxValue: this.pctMax,
|
||
designedMinStep: 1,
|
||
unit: "%"
|
||
});
|
||
}
|
||
|
||
if( this.hasHue == true || this.hasRGB == true ) {
|
||
cTypes.push({
|
||
cType: types.HUE_CTYPE,
|
||
onRegister: function(characteristic) {
|
||
characteristic.eventEnabled = true;
|
||
FHEM_subscribe(characteristic, that.name+'-hue', that);
|
||
},
|
||
onUpdate: function(value) { that.command('hue', value); },
|
||
onRead: function(callback) {
|
||
that.query('hue', function(hue){
|
||
callback(hue);
|
||
});
|
||
},
|
||
perms: ["pw","pr","ev"],
|
||
format: "int",
|
||
initialValue: 0,
|
||
supportEvents: true,
|
||
supportBonjour: false,
|
||
manfDescription: "Adjust the Hue of the Light",
|
||
designedMinValue: 0,
|
||
designedMaxValue: 360,
|
||
designedMinStep: 1,
|
||
unit: "arcdegrees"
|
||
});
|
||
}
|
||
|
||
if( this.hasSat == true ) {
|
||
cTypes.push({
|
||
cType: types.SATURATION_CTYPE,
|
||
onRegister: function(characteristic) {
|
||
characteristic.eventEnabled = true;
|
||
FHEM_subscribe(characteristic, that.name+'-sat', that);
|
||
},
|
||
onUpdate: function(value) { that.command('sat', value); },
|
||
onRead: function(callback) {
|
||
that.query('sat', function(sat){
|
||
callback(sat);
|
||
});
|
||
},
|
||
perms: ["pw","pr","ev"],
|
||
format: "int",
|
||
initialValue: 100,
|
||
supportEvents: true,
|
||
supportBonjour: false,
|
||
manfDescription: "Adjust the Saturation of the Light",
|
||
designedMinValue: 0,
|
||
designedMaxValue: 100,
|
||
designedMinStep: 1,
|
||
unit: "%"
|
||
});
|
||
}
|
||
|
||
//FIXME: parse range and set designedMinValue & designedMaxValue & designedMinStep
|
||
if( match = this.PossibleSets.match(/\bVolume\b/) ) {
|
||
cTypes.push({
|
||
cType: types.OUTPUTVOLUME_CTYPE,
|
||
onUpdate: function(value) { that.delayed('volume', value); },
|
||
onRegister: function(characteristic) {
|
||
//characteristic.eventEnabled = true;
|
||
//FHEM_subscribe(characteristic, that.name+'-Volume', that);
|
||
},
|
||
onRead: function(callback) {
|
||
that.query('Volume', function(vol){
|
||
callback(vol);
|
||
});
|
||
},
|
||
perms: ["pw","pr","ev"],
|
||
format: "int",
|
||
initialValue: 10,
|
||
supportEvents: true,
|
||
supportBonjour: false,
|
||
manfDescription: "Adjust the Volume of this device",
|
||
designedMinValue: 0,
|
||
designedMaxValue: 100,
|
||
designedMinStep: 1
|
||
//unit: "%"
|
||
});
|
||
}
|
||
|
||
//FIXME: parse range and set designedMinValue & designedMaxValue & designedMinStep
|
||
if( this.isBlind ) {
|
||
cTypes.push({
|
||
cType: types.WINDOW_COVERING_TARGET_POSITION_CTYPE,
|
||
onUpdate: function(value) { that.delayed('targetPosition', value, 1500); },
|
||
//onRegister: function(characteristic) {
|
||
// characteristic.eventEnabled = true;
|
||
// FHEM_subscribe(characteristic, that.name+'-'+that.isBlind, that);
|
||
//},
|
||
onRead: function(callback) {
|
||
that.query(that.isBlind, function(pct){
|
||
callback(pct);
|
||
});
|
||
},
|
||
perms: ["pw","pr","ev"],
|
||
format: "int",
|
||
initialValue: 0,
|
||
//initialValue: that.query( that.isBlind ),
|
||
supportEvents: false,
|
||
supportBonjour: false,
|
||
manfDescription: "Target Blind Position",
|
||
designedMinValue: 0,
|
||
designedMaxValue: 100,
|
||
designedMinStep: 1,
|
||
unit: "%"
|
||
});
|
||
cTypes.push({
|
||
cType: types.WINDOW_COVERING_CURRENT_POSITION_CTYPE,
|
||
onRegister: function(characteristic) {
|
||
characteristic.eventEnabled = true;
|
||
FHEM_subscribe(characteristic, that.name+'-'+that.isBlind, that);
|
||
},
|
||
onRead: function(callback) {
|
||
that.query(that.isBlind, function(pos){
|
||
callback(pos);
|
||
});
|
||
},
|
||
perms: ["pr","ev"],
|
||
format: "int",
|
||
initialValue: 0,
|
||
//initialValue: that.query( that.isBlind ),
|
||
supportEvents: true,
|
||
supportBonjour: false,
|
||
manfDescription: "Current Blind Position",
|
||
designedMinValue: 0,
|
||
designedMaxValue: 100,
|
||
designedMinStep: 1,
|
||
unit: "%"
|
||
});
|
||
cTypes.push({
|
||
cType: types.WINDOW_COVERING_OPERATION_STATE_CTYPE,
|
||
onRegister: function(characteristic) {
|
||
if( that.hasMotor ) {
|
||
characteristic.eventEnabled = true;
|
||
FHEM_subscribe(characteristic, that.name+'-'+that.hasMotor, that);
|
||
}
|
||
},
|
||
onRead: function(callback) {
|
||
if( that.hasMotor )
|
||
that.query(that.hasMotor, function(state){
|
||
callback(state);
|
||
});
|
||
},
|
||
perms: ["pr","ev"],
|
||
format: "int",
|
||
initialValue: 2,
|
||
supportEvents: false,
|
||
supportBonjour: false,
|
||
manfDescription: "Position State",
|
||
designedMinValue: 0,
|
||
designedMaxValue: 2,
|
||
designedMinStep: 1,
|
||
});
|
||
}
|
||
|
||
//FIXME: parse range and set designedMinValue & designedMaxValue & designedMinStep
|
||
if( this.isThermostat ) {
|
||
cTypes.push({
|
||
cType: types.TARGET_TEMPERATURE_CTYPE,
|
||
onUpdate: function(value) { that.delayed('targetTemperature', value, 1500); },
|
||
onRegister: function(characteristic) {
|
||
characteristic.eventEnabled = true;
|
||
FHEM_subscribe(characteristic, that.name+'-'+that.isThermostat, that);
|
||
},
|
||
onRead: function(callback) {
|
||
that.query(that.isThermostat, function(temperature){
|
||
callback(temperature);
|
||
});
|
||
},
|
||
perms: ["pw","pr","ev"],
|
||
format: "float",
|
||
initialValue: 20,
|
||
supportEvents: false,
|
||
supportBonjour: false,
|
||
manfDescription: "Target Temperature",
|
||
designedMinValue: 5.0,
|
||
designedMaxValue: 30.0,
|
||
//designedMinStep: 0.5,
|
||
unit: "celsius"
|
||
});
|
||
cTypes.push({
|
||
cType: types.CURRENTHEATINGCOOLING_CTYPE,
|
||
perms: ["pr","ev"],
|
||
format: "int",
|
||
initialValue: 0,
|
||
supportEvents: false,
|
||
supportBonjour: false,
|
||
manfDescription: "Current Mode",
|
||
designedMaxLength: 1,
|
||
designedMinValue: 0,
|
||
designedMaxValue: 2,
|
||
designedMinStep: 1,
|
||
});
|
||
cTypes.push({
|
||
cType: types.TARGETHEATINGCOOLING_CTYPE,
|
||
onUpdate: function(value) { that.command('targetMode', value); },
|
||
perms: ["pw","pr","ev"],
|
||
format: "int",
|
||
initialValue: 0,
|
||
supportEvents: false,
|
||
supportBonjour: false,
|
||
manfDescription: "Target Mode",
|
||
designedMinValue: 0,
|
||
designedMaxValue: 3,
|
||
designedMinStep: 1,
|
||
});
|
||
|
||
cTypes.push({
|
||
cType: types.TEMPERATURE_UNITS_CTYPE,
|
||
perms: ["pr","ev"],
|
||
format: "int",
|
||
initialValue: 0,
|
||
supportEvents: false,
|
||
supportBonjour: false,
|
||
manfDescription: "Unit",
|
||
});
|
||
}
|
||
|
||
if( this.isWindow ) {
|
||
cTypes.push({
|
||
cType: types.CONTACT_SENSOR_STATE_CTYPE,
|
||
onRegister: function(characteristic) {
|
||
characteristic.eventEnabled = true;
|
||
FHEM_subscribe(characteristic, that.name+'-contact', that);
|
||
},
|
||
onRead: function(callback) {
|
||
that.query('contact', function(state){
|
||
callback(state);
|
||
});
|
||
},
|
||
perms: ["pr","ev"],
|
||
format: "bool",
|
||
initialValue: 0,
|
||
supportEvents: false,
|
||
supportBonjour: false,
|
||
manfDescription: "Contact State",
|
||
designedMaxLength: 1
|
||
});
|
||
} else if( this.isContactSensor ) {
|
||
cTypes.push({
|
||
cType: types.CONTACT_SENSOR_STATE_CTYPE,
|
||
onRegister: function(characteristic) {
|
||
characteristic.eventEnabled = true;
|
||
FHEM_subscribe(characteristic, that.name+'-contact', that);
|
||
},
|
||
onRead: function(callback) {
|
||
that.query('contact', function(state){
|
||
callback(state);
|
||
});
|
||
},
|
||
perms: ["pr","ev"],
|
||
format: "bool",
|
||
initialValue: 0,
|
||
supportEvents: false,
|
||
supportBonjour: false,
|
||
manfDescription: "Contact State",
|
||
designedMaxLength: 1
|
||
});
|
||
}
|
||
|
||
if( this.isOccupancySensor ) {
|
||
cTypes.push({
|
||
cType: types.OCCUPANCY_DETECTED_CTYPE,
|
||
onRegister: function(characteristic) {
|
||
characteristic.eventEnabled = true;
|
||
FHEM_subscribe(characteristic, that.name+'-state', that);
|
||
},
|
||
onRead: function(callback) {
|
||
that.query('state', function(state){
|
||
callback(state);
|
||
});
|
||
},
|
||
perms: ["pr","ev"],
|
||
format: "bool",
|
||
initialValue: 0,
|
||
supportEvents: false,
|
||
supportBonjour: false,
|
||
manfDescription: "Occupancy State",
|
||
designedMaxLength: 1
|
||
});
|
||
}
|
||
|
||
if( this.hasTemperature ) {
|
||
cTypes.push({
|
||
cType: types.CURRENT_TEMPERATURE_CTYPE,
|
||
onRegister: function(characteristic) {
|
||
characteristic.eventEnabled = true;
|
||
FHEM_subscribe(characteristic, that.name+'-'+that.hasTemperature, that);
|
||
},
|
||
onRead: function(callback) {
|
||
that.query(that.hasTemperature, function(temperature){
|
||
callback(temperature);
|
||
});
|
||
},
|
||
perms: ["pr","ev"],
|
||
format: "float",
|
||
initialValue: 20,
|
||
supportEvents: true,
|
||
supportBonjour: false,
|
||
manfDescription: "Current Temperature",
|
||
unit: "celsius"
|
||
});
|
||
}
|
||
|
||
if( this.hasHumidity ) {
|
||
cTypes.push({
|
||
cType: types.CURRENT_RELATIVE_HUMIDITY_CTYPE,
|
||
onRegister: function(characteristic) {
|
||
characteristic.eventEnabled = true;
|
||
FHEM_subscribe(characteristic, that.name+'-humidity', that);
|
||
},
|
||
onRead: function(callback) {
|
||
that.query('humidity', function(humidity){
|
||
callback(humidity);
|
||
});
|
||
},
|
||
perms: ["pr","ev"],
|
||
format: "int",
|
||
initialValue: 50,
|
||
designedMinValue: 0,
|
||
designedMaxValue: 100,
|
||
supportEvents: true,
|
||
supportBonjour: false,
|
||
manfDescription: "Current Humidity",
|
||
unit: "%"
|
||
});
|
||
|
||
}
|
||
|
||
return cTypes;
|
||
},
|
||
|
||
sType: function() {
|
||
if( match = this.PossibleSets.match(/\bvolume\b/) ) {
|
||
return types.SPEAKER_STYPE;
|
||
} else if( this.isBlind ) {
|
||
return types.WINDOW_COVERING_STYPE;
|
||
} else if( this.isThermostat ) {
|
||
return types.THERMOSTAT_STYPE;
|
||
} else if( this.isWindow ) {
|
||
return types.CONTACT_SENSOR_STYPE;
|
||
} else if( this.isContactSensor ) {
|
||
return types.CONTACT_SENSOR_STYPE;
|
||
} else if( this.isOccupancySensor ) {
|
||
return types.OCCUPANCY_SENSOR_STYPE;
|
||
} else if( this.isLight || this.hasPct || this.hasHue || this.hasRGB ) {
|
||
return types.LIGHTBULB_STYPE;
|
||
} else if( this.hasTemperature ) {
|
||
return types.TEMPERATURE_SENSOR_STYPE;
|
||
} else if( this.hasHumidity ) {
|
||
return types.HUMIDITY_SENSOR_STYPE;
|
||
} else {
|
||
return types.SWITCH_STYPE;
|
||
}
|
||
},
|
||
|
||
getServices: function() {
|
||
var that = this;
|
||
var services = [{
|
||
sType: types.ACCESSORY_INFORMATION_STYPE,
|
||
characteristics: this.informationCharacteristics(),
|
||
},
|
||
{
|
||
sType: this.sType(),
|
||
characteristics: this.controlCharacteristics(that)
|
||
}];
|
||
this.log("Loaded services for " + this.name)
|
||
return services;
|
||
}
|
||
};
|
||
|
||
//module.exports.accessory = FHEMAccessory;
|
||
module.exports.platform = FHEMPlatform;
|