redmatic-homekit
Version:
HAP-Nodejs based Node-RED nodes to create HomeKit Accessories
182 lines (153 loc) • 6.77 kB
JavaScript
/* eslint-disable no-new */
const Accessory = require('./accessory');
function createService(channel) {
let intermediatePosition; // 0-100
let LEVEL = 0; // 0.0-1.0
let LEVEL_2 = 0; // 0.0-1.0
const channelIndex = channel.channel.split(':')[1];
this.ccu.subscribe({
datapointName: this.config.deviceAddress + ':' + channelIndex + '.LEVEL',
cache: true,
stable: false
}, msg => {
intermediatePosition = msg.value * 100;
});
const service = this.addService('WindowCovering', channel.name, channelIndex);
service
.get('CurrentPosition', this.config.deviceAddress + ':' + channelIndex + '.LEVEL', value => {
LEVEL = value;
intermediatePosition = value * 100;
return LEVEL * 100;
})
.get('TargetPosition', this.config.deviceAddress + ':' + channelIndex + '.LEVEL', value => {
if (typeof LEVEL === 'undefined') {
LEVEL = value;
}
return LEVEL * 100;
})
.set('TargetPosition', (value, callback) => {
LEVEL = value / 100;
if (value === 0 && intermediatePosition === 0) {
intermediatePosition = 1;
} else if (value === 100 && intermediatePosition === 100) {
intermediatePosition = 99;
}
this.node.debug(channel.name + ' intermediatePosition ' + intermediatePosition);
service.update('CurrentPosition', intermediatePosition);
const params = {
LEVEL
};
if (channel.tilt) {
params.LEVEL_2 = LEVEL_2;
}
if (channel.tilt || this.config.type === 'BLIND_VIRTUAL_RECEIVER') {
if (LEVEL === 0) {
params.LEVEL_2 = 0;
}
if (LEVEL === 1) {
params.LEVEL_2 = 1;
}
}
this.node.debug('set ' + this.config.name + ' (WindowCovering) TargetPosition ' + value + ' -> ' + this.config.description.ADDRESS + ':' + channelIndex + ' ' + JSON.stringify(params));
this.ccu.methodCall(this.config.iface, 'putParamset', [this.config.description.ADDRESS + ':' + channelIndex, 'VALUES', params])
.then(() => {
callback();
})
.catch(() => {
callback(new Error(this.hap.HAPServer.Status.SERVICE_COMMUNICATION_FAILURE));
});
})
.get('PositionState', this.config.deviceAddress + ':' + channelIndex + '.ACTIVITY_STATE', (value, c) => {
switch (value) {
case 1:
return c.INCREASING;
case 2:
return c.DECREASING;
default:
return c.STOPPED;
}
});
if (channel.tilt) {
service
.get('CurrentVerticalTiltAngle', this.config.deviceAddress + ':' + channelIndex + '.LEVEL_2', value => {
LEVEL_2 = value;
value = (value * 180) - 90;
this.node.debug('get CurrentVerticalTiltAngle ' + this.config.name + ' LEVEL_2 ' + LEVEL_2 + ' ' + value);
return value;
})
.get('TargetVerticalTiltAngle', this.config.deviceAddress + ':' + channelIndex + '.LEVEL_2', value => {
LEVEL_2 = value;
value = (value * 180) - 90;
this.node.debug('get TargetVerticalTiltAngle ' + this.config.name + ' LEVEL_2 ' + LEVEL_2 + ' ' + value);
return value;
})
.set('TargetVerticalTiltAngle', (value, callback) => {
LEVEL_2 = (value + 90) / 180;
this.node.debug('set TargetVerticalTiltAngle ' + this.config.name + ' LEVEL_2 ' + LEVEL_2 + ' ' + value);
const params = {
LEVEL,
LEVEL_2
};
this.node.debug('set ' + channel.name + ' (WindowCovering) TargetVerticalTiltAngle ' + value + ' -> ' + this.config.description.ADDRESS + ':' + channelIndex + ' ' + JSON.stringify(params));
this.ccu.methodCall(this.config.iface, 'putParamset', [this.config.description.ADDRESS + ':' + channelIndex, 'VALUES', params])
.then(() => {
callback();
})
.catch(() => {
callback(new Error(this.hap.HAPServer.Status.SERVICE_COMMUNICATION_FAILURE));
});
});
}
}
class GenericHmipBlindAcc extends Accessory {
constructor(config, node, channels) {
super(config, node);
this.channels = channels;
node.debug('creating accessory for ' + channels.length + ' channels');
}
init() {
this.channels.forEach(channel => {
createService.call(this, channel);
});
}
}
class GenericHmipBlind {
constructor(config, node) {
const {ccu} = node;
this.ccu = ccu;
this.config = config;
let acc = 0;
let pos = 0;
const channels = [];
this.config.description.CHILDREN.forEach(channel => {
const desc = this.ccu.metadata.devices['HmIP-RF'][channel];
if (desc.TYPE === 'BLIND_VIRTUAL_RECEIVER' || desc.TYPE === 'SHUTTER_VIRTUAL_RECEIVER') {
if (!channels[acc]) {
channels[acc] = [];
}
const name = this.ccu.channelNames[channel];
const tilt = desc.TYPE === 'BLIND_VIRTUAL_RECEIVER' &&
(!this.config.options[channel] || this.config.options[channel].type !== 'VerticalTilt Disabled');
if (pos === 0) {
if (!this.config.options[channel] || !this.config.options[channel].disabled) {
channels[acc].push({channel, name, tilt});
}
} else if (this.config.options[channel] && this.config.options[channel].enabled) {
channels[acc].push({channel, name, tilt, type: desc.TYPE});
}
if (++pos > 2) {
pos = 0;
acc += 1;
}
}
});
channels.forEach(accChannels => {
if (accChannels.length > 0) {
const conf = Object.assign({}, config, {name: accChannels[0].name});
conf.description = Object.assign({}, config.description, {ADDRESS: accChannels[0].channel});
new GenericHmipBlindAcc(conf, node, accChannels);
}
});
}
}
module.exports = GenericHmipBlind;