Skip to content

Commit

Permalink
add virtual heating groups (#131)
Browse files Browse the repository at this point in the history
  • Loading branch information
hobbyquaker committed Apr 17, 2019
1 parent 0733a10 commit 73bd263
Show file tree
Hide file tree
Showing 3 changed files with 414 additions and 0 deletions.
220 changes: 220 additions & 0 deletions homematic-devices/hm-cc-vg-1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
const Accessory = require('./lib/accessory');

module.exports = class HmCcVg1 extends Accessory {
init(config, node) {
const {bridgeConfig, ccu} = node;
const {hap} = bridgeConfig;

let valueSetpoint;

let controlMode;
let target;

const levels = {};
let level = 0;

const that = this;

const valveDevices = [];

const lowbatDps = {};

let valueDevice;

const group = ccu.groups && ccu.groups[config.deviceAddress.replace(/VirtualDevices\.INT0*/, '')];

if (!group) {
node.error(config.deviceAddress + ' group configuration not found');
return;
}

node.debug(config.deviceAddress + ' ' + group.groupProperties.NAME + ' ' + group.groupType.id + ' has ' + group.groupMembers.length + ' members');
group.groupMembers.forEach(member => {
if (member.memberType.id.startsWith('HM-CC')) {
valveDevices.push('BidCos-RF.' + member.id);
valueDevice = valueDevice || ('BidCos-RF.' + member.id);
} else if (member.memberType.id.startsWith('HM-TC')) {
valueDevice = 'BidCos-RF.' + member.id;
}

lowbatDps['BidCos-RF.' + member.id + ':0.LOWBAT'] = false;
node.debug(config.deviceAddress + ' member ' + member.memberType.id + ' ' + member.id);
});

if (valueDevice) {
node.debug(config.deviceAddress + ' valueDevice ' + valueDevice);
} else {
node.error(config.deviceAddress + ' group has neither thermostat nor valve device');
return;
}

function targetState() {
// 0=off, 1=heat, 3=auto
switch (controlMode) {
case 1:
// Manu
target = valueSetpoint > 4.5 ? 1 : 0;
break;
case 0:
// Auto
target = 3;
break;
case 2:
// Party
target = 3;
break;
case 3:
// Boost
// don't change targetState so we can switch back to previous state when boost mode is deactivated
break;
default:
}

return controlMode === 3 ? 1 : target;
}

function currentState() {
// 0=off, 1=heat
return (level > 0 || controlMode === 3) ? 1 : 0;
}

const serviceThermostat = this.addService('Thermostat', config.name);
const subtypeThermostat = serviceThermostat.subtype;

serviceThermostat
.setProps('CurrentTemperature', {minValue: -40, maxValue: 80})
.get('CurrentTemperature', valueDevice + ':2.ACTUAL_TEMPERATURE')

.setProps('TargetTemperature', {minValue: 4.5, maxValue: 30.5, minStep: 0.5})
.get('TargetTemperature', valueDevice + ':2.SET_TEMPERATURE', value => {
valueSetpoint = value;
updateHeatingCoolingState();
return value;
})
.set('TargetTemperature', config.deviceAddress + ':1.SET_TEMPERATURE')

.setProps('CurrentHeatingCoolingState', {validValues: [0, 1], maxValue: 1})
.get('CurrentHeatingCoolingState', valueDevice + ':2.SET_TEMPERATURE', () => {
setTimeout(() => {
updateHeatingCoolingState();
}, 1000);
return currentState();
})

.setProps('TargetHeatingCoolingState', {validValues: [0, 1, 3]})
.get('TargetHeatingCoolingState', valueDevice + ':2.SET_TEMPERATURE', () => {
setTimeout(() => {
updateHeatingCoolingState();
}, 1000);
return targetState();
})
.set('TargetHeatingCoolingState', (value, callback) => {
// 0=off, 1=heat, 3=auto
if (value === 0) {
ccu.setValue(config.iface, config.description.ADDRESS + ':1', 'MANU_MODE', 4.5)
.then(() => {
controlMode = 1;
callback();
})
.catch(() => {
callback(new Error(hap.HAPServer.Status.SERVICE_COMMUNICATION_FAILURE));
});
} else if (value === 1) {
ccu.setValue(config.iface, config.description.ADDRESS + ':1', 'MANU_MODE', 21)
.then(() => {
controlMode = 1;
callback();
})
.catch(() => {
callback(new Error(hap.HAPServer.Status.SERVICE_COMMUNICATION_FAILURE));
});
} else {
ccu.setValue(config.iface, config.description.ADDRESS + ':1', 'AUTO_MODE', true)
.then(() => {
controlMode = 0;
callback();
})
.catch(() => {
callback(new Error(hap.HAPServer.Status.SERVICE_COMMUNICATION_FAILURE));
});
}
});

function updateHeatingCoolingState() {
const current = currentState();
node.debug('update ' + config.name + ' (' + subtypeThermostat + ') CurrentHeatingCoolingState ' + current);
that.acc.getService(subtypeThermostat).updateCharacteristic(hap.Characteristic.CurrentHeatingCoolingState, current);
const target = targetState();
node.debug('update ' + config.name + ' (' + subtypeThermostat + ') TargetHeatingCoolingState ' + target);
that.acc.getService(subtypeThermostat).updateCharacteristic(hap.Characteristic.TargetHeatingCoolingState, target);
}

valveDevices.forEach(valveStateDevice => {
const datapointLevel = valveStateDevice + ':4.VALVE_STATE';
this.subscribe(datapointLevel, value => {
levels[datapointLevel] = value;
let max = 0;
Object.keys(levels).forEach(dp => {
if (levels[dp] > max) {
max = levels[dp];
}
});
if (level !== max) {
level = max;
node.debug('update ' + config.name + ' level ' + level);
updateHeatingCoolingState();
}
});
});

this.subscriptions.push(ccu.subscribe({
cache: true,
change: true,
datapointName: valueDevice + ':2.CONTROL_MODE'
}, msg => {
controlMode = msg.value;
node.debug('update ' + config.name + ' controlMode ' + msg.value);
updateHeatingCoolingState();
}));

const batteryService = this.addService('BatteryService', config.name, 'Battery');
Object.keys(lowbatDps).forEach(dp => {
this.subscribe(dp, value => {
lowbatDps[dp] = value;
let lowbat = false;
Object.keys(lowbatDps).forEach(ldp => {
lowbat = lowbat || lowbatDps[ldp];
});
batteryService.update('StatusLowBattery', lowbat ? hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_LOW : hap.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL);
});
});

if (this.option('HumiditySensor')) {
this.addService('HumiditySensor', config.name, 'HumiditySensor')
.get('CurrentRelativeHumidity', valueDevice + ':2.ACTUAL_HUMIDITY');
}

if (this.option('BoostSwitch')) {
this.addService('Switch', 'Boost ' + config.name, 'Boost')
.set('On', (value, callback) => {
let dp;
if (value) {
dp = 'BOOST_MODE';
} else if (target === 0 || target === 1) {
dp = 'MANU_MODE';
value = valueSetpoint;
} else {
dp = 'AUTO_MODE';
value = true;
}

this.ccuSetValue(config.iface + '.' + config.description.ADDRESS + ':1.' + dp, value, res => {
callback(res);
});
})
.get('On', valueDevice + ':2.CONTROL_MODE', value => {
return value === 3;
});
}
}
};
Loading

0 comments on commit 73bd263

Please sign in to comment.