I know it’s an older discussion, but I’ve written a Homey script and combined with a couple of flows (explained in the script) I have set up my zone heating this way and it works nice and smooth.
Still busy on refining it. My script works with Plugwise Anna as thermostat and 12 Eurotronic Spirit valves in 9 different zones.
If you set one of the Spirit valves to a new temp, the script will be executed and decide if the Anna needs to fire up the boiler. Anna uses OpenTherm, so Anna decides if it’s needs to boost or just use cosy flames.
Please, feel free to comment on my script for improvements!
Thanks to mrtaunus for sharing his example (https://github.com/athombv/com.athom.homeyscript/wiki/example:-ThermostatTriggered.js)
/*
* Zone heating script by Maarten Meijer
* dd 2020-1-6
* for Anna thermostat and Eurotronic Valves
*
* Set the valve temperature treshold to at least 0.5 to prevent running this script for every 0.1 temp-change.
* Do some temp calibration to your valves, most of my valves had a 1.5 degrees offset on the measured temperature.
*
* create 2 flows per zone:
* WHEN request_temp from valve in ZONE-X changes THEN run script with argument ZONE-X,set
* WHEN temp from valve in ZONE-X changes THEN run script with argument ZONE-X,need
*
* If you have more then one radiator in a zone, then set it up as a master-slave
* WHEN request_temp from valve-1 in ZONE-X changes THEN change request_temp from valve-2 in ZONE-X
*
*/
let debug = false;
let arguments = args[0].split(",");
let zoneName = arguments[0];// First argument is the zone that's doing the request;
let requestType = arguments[1];// Second argument is the trigger type, set is when a valve is set to a target temp, need is when temperature changes.
// The offset temp is used to overcome temperature differnces between valves and thermostat
// To be sure the thermostat heats up to provide heat to the selected valve
var offSetTemp = 1
if(requestType == "set"){ //for different offsets between set/need, use for boost
var offSetTemp = 1;
}
let hysteresis = 0.5; // Used to prevent switching on/off for every 0.5 degrees
var logText = ""; //future variable for posting to Homey timeline
log('----------------');
log('Call from zone:', arguments[0], ' type:', arguments[1]);
log('----------------');
// Get all zones and devices
let zones = await Homey.zones.getZones();
let devices = await Homey.devices.getDevices();
// Set requesting zone according to Args
let zone = getZone(zones, zoneName);
let thermostat;
let valves = [];
let otherValves = [];
//Find Anna Thermostat by driverID, Anna can be in any zone
Object.values(devices).forEach(device => {
if (device.driverId === 'anna') {
thermostat = device;
if (debug) {
log('Thermostat:', thermostat.name, '( zone:',thermostat.zoneName,', type:', thermostat.class, ')');
}
}
});
if (debug) {
log('----------------');
}
// Find Valves in specified Zone and put the other ones in the otherValves array
Object.values(devices).forEach(device => {
if (deviceInZone(device.zone, zone)) {
if (device.driverId === 'spirit_zwave') { //} && device.virtualClass === 'heater') {
valves.push(device);
if (debug) {
log('Valve:', device.name, '( zone:',device.zoneName,')');
}
}
} else {
if (device.driverId === 'spirit_zwave') { //} && device.virtualClass === 'heater') {
otherValves.push(device);
if (debug) {
log('Valve in different zone:', device.name, '( zone:',device.zoneName,')');
}
}
}
});
if (debug) {
log('----------------');
}
var thermostatTemp = thermostat.capabilitiesObj.target_temperature.value; //current target temperature Thermostat Anna
var zoneRequestTemp = valves[0].capabilitiesObj.target_temperature.value; //requested temp master Valve in zone
// Loop trough the other valves to see if one has requested a higher temperature to prevent cooling down being initiated by this request
var otherTemp = 0;
var highestOtherTemp = 0;
Object.values(otherValves).forEach(otherValve => {
otherTemp = otherValve.capabilitiesObj.target_temperature.value;
if(otherTemp > highestOtherTemp) {
highestOtherTemp = otherTemp;
}
});
log(highestOtherTemp);
log(new Date().toISOString(), 'Thermostat temp:', thermostatTemp, 'Zone request temp:', zoneRequestTemp, 'Hysteresis:', hysteresis);
// If thermostat is already set to a near value, decide what to do.
if (thermostatTemp >= zoneRequestTemp + hysteresis) {
//if(highestOtherTemp <= zoneRequestTemp) {
callForHeatOrCool(false, valves);
//}
}
else if (thermostatTemp < zoneRequestTemp - hysteresis) {
callForHeatOrCool(true, valves);
}
return true;
// ########################################################################
function getZone(zones, zoneName) {
let retVal;
Object.values(zones).forEach(z => {
if (z.name === zoneName) {
retVal = z;
}
});
return retVal;
}
function deviceInZone(deviceZone, matchZone) {
if (deviceZone == matchZone.id) {
return true;
}
else if (deviceZone.parent) {
return deviceInZone(zones[deviceZone.parent], matchZone);
}
}
if (debug) {
log('----------------');
}
function callForHeatOrCool(heat, radiatorRelays) {
Object.values(valves).forEach(valve => {
valve.setCapabilityValue('onoff', heat);
if (heat) {
log("Call for HEAT from ", thermostat.name, ' to ', valve.name);
thermostat.setCapabilityValue('target_temperature', zoneRequestTemp + offSetTemp);
}
else {
log("Call for COOL from ", thermostat.name, ' to ', valve.name);
thermostat.setCapabilityValue('target_temperature', zoneRequestTemp - offSetTemp);
}
});
}