DIY (semi) zone heating using Eurotronic SpiritZ

Moderator: leecollings

Post Reply
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

DIY (semi) zone heating using Eurotronic SpiritZ

Post by rrozema »

Goal:
Turn my central heating system into a zone-heating system: every room can set their own temperature. There are some proprietary systems, but I think these are too expensive and require me to replace almost my entire heating system. I think I can do it cheaper myself. This is a work in progress. I may describe the entire project in more detail at some later time, but for now I just want to share my idea and the dzvents script.

The idea is that I have some Eurotronic SpiritZ radiator valves and these control the heating per room. The SpiritZ however only opens and closes the radiator valve, the boiler still needs to be switched on to actually provide hot water to the radiator. Typically this is done by a thermostat in the living room. If it gets cold(er) in the living room, the boiler is switched on and hot water starts flowing. As long as the living room is warm enough however, also the other rooms will get no hot water and thus will remain cold. My challenge was to think of a way to make the boiler provide hot water when one of the rooms needs it. I currently only have a SpiritZ in the bathroom and another in the kitchen, but I am planning of putting one in every room (*).

I use SVT to control my boiler. SVT needs one ore more temperature sensors, plus one or more switch devices that it can use to switch the boiler on and off. The SVT plugin also adds a temperature setpoint device to Domoticz that can be used to set the required temperature. Basically what SVT does is switch on the boiler at time intervals to make sure the temperature returned by the temperature sensors is close to the temperature set in the setpoint device. I use a cheap wifi relay (1,79 euro) as the switch to control my boiler.

Now what I need to do is:
1 - make sure SVT's setpoint is set so that every room can be heated up to the temperature set in the SpiritZ for that room and
2 - make sure SVT knows if the temperature is at or close to the temperatures set in the SpiritZ valves.
3 - switch off the boiler if no SpiritZ requires hot water.

I made SVT read the temperature from a dummy temperature sensor that gets updated using my script. Plus I make my script update SVT's setpoint. To calculate both the setpoint and the temperature for SVT I use the valve opening reported by each SpiritZ.

The setpoint is easiest: SVT's setpoint is set to the highest setpoint of all SpiritZ's that have a valve opening higher than 0%. This way SVT will always deliver water hot enough to heat up to the highest requested temperature. A room that is already at it's temperature set in the SpiritZ will not be heated higher than needed because the valve will be closed for that room. My family will only change the SpiritZ's setpoints, SVT's setpoint is calculated from those.

The measured temperature is a bit more complicated: I calculate the average of all room's temperatures, but I assign a weight to each of those values: the opening percentage of the valve in that room. If the difference between the room's setpoint and the actual temperature is high, the valve will be wide open (100%), if the room temperature is close to the setpoint, the valve will be open only a little and if the room temperature is over the setpoint, the valve will closed, i.e. at 0%. So what I do is multiply the room temperature by the valve percentage, then calculate the sum for all rooms. This sum I then divide by the sum of all room's valve percentages (each ranging from 0 to 100). A room that is at or above it's setpoint will have 0, i.e. it's temperature is not used in the calculation for SVT's temperature, a room that is far below it's setpoint will heavily influence SVT's temperature, a room will have less influence on SVT's temperature the closer it comes to it's setpoint, until the valve closes and it doesn't influence the temperature at all.

I also added a correction on the valve opening percentage to compensate for a valve that doesn't completely open (100%) or close (0%) due to physical limitations. But that wasn't needed at all, as the SpiritZ apparently automatically calibrates the valve opening to go all the way from 0% to 100%, both my installed valves go to both extremes.

I am going to clean the script up later. Probably going to do a full rewrite once I've got more valves installed (I don't have the hardware installed yet) and am past this initial phase of experimenting, but until the I still want to share this preliminary script with you all:

Code: Select all

local KEUKEN_VALVE_NAME = "Keuken: Radiator: Valve Opening"
local KEUKEN_SENSOR_NAME = "Keuken: Radiator: Air Temperature"
local KEUKEN_SENSOR2_NAME = "Keuken: TempHum"
local KEUKEN_SETPOINT_NAME = "Keuken: Radiator: Heat"

local BADKAMER_VALVE_NAME = "Badkamer: Radiator: Valve Opening"
local BADKAMER_SENSOR_NAME = "Badkamer: Radiator: Air Temperature"
local BADKAMER_SENSOR2_NAME = "Badkamer: TempHum"
local BADKAMER_SETPOINT_NAME = "Badkamer: Radiator: Heat"

local WOONKAMER_SENSOR_NAME = "Woonkamer: TempHum"
local WOONKAMER_SETPOINT_NAME = "Woonkamer: Verwarming"


local VERWARMING_SETPOINT_NAME = "Verwarming"
local VERWARMING_TEMPERATURE_NAME = "Verwarming: Temperature"


return {
	on = {
	    devices = {
	        BADKAMER_VALVE_NAME,
	        KEUKEN_VALVE_NAME,
	        
	        BADKAMER_SENSOR_NAME,
	        BADKAMER_SENSOR2_NAME,
	        KEUKEN_SENSOR_NAME,
	        KEUKEN_SENSOR2_NAME,
	        WOONKAMER_SENSOR_NAME,
	        
	        BADKAMER_SETPOINT_NAME,
	        KEUKEN_SETPOINT_NAME,
	        WOONKAMER_SETPOINT_NAME
	    }
	},
	data = {
	    minimum_opening = { initial = {} },
	    maximum_opening = { initial = {} }
	},
    execute = function(domoticz, device)

        if (true == device.isDevice) then
            domoticz.log('Device ' .. device.name, domoticz.LOG_ERROR)
            
            local keuken_valve = domoticz.devices(KEUKEN_VALVE_NAME)
            local keuken_sensor = domoticz.devices(KEUKEN_SENSOR_NAME)
            local keuken_sensor2 = domoticz.devices(KEUKEN_SENSOR2_NAME)
            local keuken_setpoint = domoticz.devices(KEUKEN_SETPOINT_NAME)
            
            local badkamer_valve = domoticz.devices(BADKAMER_VALVE_NAME)
            local badkamer_sensor = domoticz.devices(BADKAMER_SENSOR_NAME)
            local badkamer_sensor2 = domoticz.devices(BADKAMER_SENSOR2_NAME)
            local badkamer_setpoint = domoticz.devices(BADKAMER_SETPOINT_NAME)

            local woonkamer_sensor = domoticz.devices(WOONKAMER_SENSOR_NAME)
            local woonkamer_setpoint = domoticz.devices(WOONKAMER_SETPOINT_NAME)
            
            local verwarming_setpoint = domoticz.devices(VERWARMING_SETPOINT_NAME)
            local verwarming_temperature = domoticz.devices(VERWARMING_TEMPERATURE_NAME)
            
            -- Depending on the way the valve got installed and the valve foot on the radiator, the valve may 
            -- close well above 0% and thus never reach that 0%. I want a range of 0 to 100% though to calculate 
            -- a proper weighted average temperature. So for this reason I keep track of the lowest opening 
            -- level ever reached for each valve, so I can do a correction on the opening level further
            -- below. The same may be true for the maximum level, but I'm going to ignore that for now.
            --
            -- Instruction to get the minimum value set fastest: Put the setpoint for each valve at an extremely 
            -- low temperature -i.e. well below the current room temperature- and leave it there for a few minutes so
            -- that the valve will close. The script will now 'learn' the lowest opening value this valve can reach.
            if (domoticz.data.minimum_opening[KEUKEN_VALVE_NAME] == nil or keuken_valve.level < domoticz.data.minimum_opening[KEUKEN_VALVE_NAME]) then
                domoticz.data.minimum_opening[KEUKEN_VALVE_NAME] = keuken_valve.level
            end
            if (domoticz.data.maximum_opening[KEUKEN_VALVE_NAME] == nil or keuken_valve.level > domoticz.data.maximum_opening[KEUKEN_VALVE_NAME]) then
                domoticz.data.maximum_opening[KEUKEN_VALVE_NAME] = keuken_valve.level
            end

            if (domoticz.data.minimum_opening[BADKAMER_VALVE_NAME] == nil or badkamer_valve.level < domoticz.data.minimum_opening[BADKAMER_VALVE_NAME]) then
                domoticz.data.minimum_opening[BADKAMER_VALVE_NAME] = badkamer_valve.level
            end
            if (domoticz.data.maximum_opening[BADKAMER_VALVE_NAME] == nil or badkamer_valve.level > domoticz.data.maximum_opening[BADKAMER_VALVE_NAME]) then
                domoticz.data.maximum_opening[BADKAMER_VALVE_NAME] = badkamer_valve.level
            end

            -- Determine the corrected weight-factor for each valve. 
            -- TODO: Add correction so that the actual opening range is from "minimum opening" to 100% again.
            local weight = {}

            if (keuken_valve.level < domoticz.data.minimum_opening[KEUKEN_VALVE_NAME] or domoticz.data.maximum_opening[KEUKEN_VALVE_NAME] <= domoticz.data.minimum_opening[KEUKEN_VALVE_NAME]) then
                weight[KEUKEN_VALVE_NAME] = 0.0
            else
                weight[KEUKEN_VALVE_NAME] = 100.0 * ((keuken_valve.level - domoticz.data.minimum_opening[KEUKEN_VALVE_NAME]) / (domoticz.data.maximum_opening[KEUKEN_VALVE_NAME] - domoticz.data.minimum_opening[KEUKEN_VALVE_NAME]))
            end
            
            if (badkamer_valve.level < domoticz.data.minimum_opening[BADKAMER_VALVE_NAME] or domoticz.data.maximum_opening[BADKAMER_VALVE_NAME] <= domoticz.data.minimum_opening[BADKAMER_VALVE_NAME]) then
                weight[BADKAMER_VALVE_NAME] = 0.0
            else
                weight[BADKAMER_VALVE_NAME] = 100.0 * ((badkamer_valve.level - domoticz.data.minimum_opening[BADKAMER_VALVE_NAME]) / (domoticz.data.maximum_opening[BADKAMER_VALVE_NAME] - domoticz.data.minimum_opening[BADKAMER_VALVE_NAME]))
            end

            -- Determine the highest setpoint that has a non-zero weight.
            local setpoints = { woonkamer_setpoint.setPoint }    -- woonkamer doesn't have a valve, it is always fully open (i.e. at 100%). 
            										  -- This safes me from installing a bypass for now :-).
            if weight[KEUKEN_VALVE_NAME] > 0 then
                table.insert( setpoints, keuken_setpoint.setPoint)
            end
            if weight[BADKAMER_VALVE_NAME] > 0 then
                table.insert( setpoints, badkamer_setpoint.setPoint)
            end
            
            local setpoint_calculated = nil
            for i, sp in ipairs( setpoints ) do
                if (setpoint_calculated == nil or sp > setpoint_calculated) then
                   setpoint_calculated = sp
                end
            end
        
            -- Now determine the weighted average of all temperature sensors
            local temperature_total = 100 * woonkamer_sensor.temperature 
                + weight[KEUKEN_VALVE_NAME] * keuken_sensor.temperature 
                + weight[KEUKEN_VALVE_NAME] * keuken_sensor2.temperature
                + weight[BADKAMER_VALVE_NAME] * badkamer_sensor.temperature
                + weight[BADKAMER_VALVE_NAME] * badkamer_sensor2.temperature

            local weight_total = (100 + weight[KEUKEN_VALVE_NAME] + weight[KEUKEN_VALVE_NAME] + weight[BADKAMER_VALVE_NAME] + weight[BADKAMER_VALVE_NAME])

            
            local weighted_temperature = math.floor((temperature_total / weight_total) * 10 + 0.5) / 10
            
            domoticz.log('Calculated setpoint: ' .. tostring(setpoint_calculated) .. ', Weighted temperature: ' .. tostring(weighted_temperature) .. ', weights: bk: ' .. weight[BADKAMER_VALVE_NAME] .. ', k: ' .. weight[KEUKEN_VALVE_NAME] .. '.', domoticz.LOG_ERROR)
            

            if (verwarming_setpoint.setPoint ~= setpoint_calculated) then
                domoticz.log('Updating setpoint: ' .. tostring(setpoint_calculated).. '.', domoticz.LOG_ERROR)
                verwarming_setpoint.updateSetPoint(setpoint_calculated)
            end
            
            if (verwarming_temperature.temperature ~= weighted_temperature) then
                domoticz.log('Updating temparature: ' .. tostring(weighted_temperature).. '.', domoticz.LOG_ERROR)
                verwarming_temperature.updateTemperature(weighted_temperature)
            end
        end
	end
}

(*) You have to be careful when you change an existing 'normal' heating system into a zone-heating system. because if all rooms are warm enough, all valves will be closed but the pump will still be running. Either your pump will break or your pipes will start leaking. Plus the boiler can not do its work efficiently if the water flowing back into the boiler is not a couple degrees cooler than the water flowing away from it. Modern HR boilers will even start giving errors if the return water is not cooler. To make sure this doesn't happen, you must either always leave one radiator open, or have a bypass valve built into your system (a bypass is an automatic valve that opens automatically if the pressure goes over some set pre-set value, thereby making sure a loop remains open when all radiators are closed) https://www.heatnet.nl/bypass.html.
Last edited by rrozema on Sunday 22 November 2020 16:50, edited 1 time in total.
iganin
Posts: 47
Joined: Tuesday 28 October 2014 17:55
Target OS: NAS (Synology & others)
Domoticz version: 2020.2
Location: Aalter, Belgium
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by iganin »

What is pros to use your logic instead of creating SVT hardware per a room?
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by rrozema »

@iganin, can you explain how you would use SVT to control both the boiler and a radiator valve? so I can do a fair comparison?

For now I am going to assume you will to put the SpiritZ in "manufacturer specific"-mode to be able to control the valve from SVT. Then let SVT control both the valve and the boiler. If you've got another idea, please let me know, so I can re-compare.

Given the above alternative, what are the pros and cons of my weighted-average zone-heating (WA) and this stand-alone SVT alternative (SVT).

1 - Let's start with the controls the end users can use to set the temperature setpoint in their room: in the (WA) solution the SpiritZ is the setpoint for their room: they can use the buttons and display on the SpiritZ to see and set it, or Domoticz (on phone, pc, etc) or even voice (using Controlicz). In the WA solution the SpiritZ isn't in control, SVT is. So SpiritZ's physical buttons are disabled and the display only shows the valve opening (I think, I haven't tried this). So the end user can only control and see their rooms setpoint from Domoticz or Controlicz. I think this is a pro for WA.

2 - Now let's look at how the temperature is regulated in each room: SVT regulates the temperature by time-modulation. If the actual temperature is a little bit below the setpoint the boiler is switched on (at 100%) for a short period, if the actual temperature is further below the setpoint, it makes the period the boiler is on longer. That boiler however is a resource shared between all rooms. So in the SVT solution, each room will individually try to switch the boiler on and off to get the temperature up. Suppose we implement an or-logic for the boiler, the boiler will burn whenever one of the SVT's decides it is time for their period of heating: The boiler will be on a lot and at irregulate intervals in the SVT solution. The valve opening is also regulated by SVT, so it is either open or closed, nothing in between. In the WA situation, the SpiritZ regulates it's own valve opening: if the temperature is a little below the setpoint, only a little water is let into the radiator. If the temperature is further below the setpoint the valve is opened further to let more water in. The boiler however is switched on and off by SVT, based on the setpoints and temperatures in all rooms together. If there is at least one room requesting hot water, SVT will run the boiler to raise the average temperature to the highest setpoint for which the valve is open. Rooms that are at their setpoint have their valve closed, so their setpoints and temperatures are ignored: the boiler is regulated to heat up only those rooms that are not at their temperature yet and rooms that are furthest from their setpoint receive the most heat. Another pro for WA, I think.

3 - battery usage: since in the WA solution the SpiritZ regulates its own valve, the differences in valve opening are most of the times very small. In the SVT solution however the valve is opened and closed all the way. The motor running the valve is -as far as I know- the part that consumes the most energy, so I think this is another pro for WA.

4 - noise: The only part of the SpiritZ that makes noises is again the motor. WA makes it do little steps only, SVT makes it go from closed to open and closed. Again a pro for WA. Also, when the valve is completely open, a lot of water will rush through the radiator. Sometimes this can be heard. In the SVT solution the valve will either be closed or fully open, in the WA solution valve open is far more gradually, so also much less water will run through. Another pro for WA.

5 - comfort: SVT, doing bursts of 100% alternated with 0% heat delivery, will have a lot of overshoot, the room going over the temperature set. WA's behaviour is much more gradual, so the risk of a room going far over or under its setpoint is much less. Pro for WA, I think. WA also heats up a single room quickly when a setpoint is raised: all of the boiler's hot water will go into that room only.

6 - reliability: z-wave implementation in Domoticz/openzwave is known to occasionally lose some commands. If this happens in the SVT solution, a valve may remain closed when it should have opened and a room will not receive the heat it needs to warm up. Far worse hower, it may remain open when it was supposed to close. The room will continue to receive hot water and will overheat. In the WA solution however the SpiritZ operates autonomously. If a command is lost, the valve will still open and close in time. Another pro for WA.

7 - energy usage: the SVT solution, running the boiler at 100% whenever at least one room needs hot water will consume a lot more energy. The WA solution currently runs the boiler at 100% too (because SVT does so), but it does so only when the weghted average temperature requires it to. In the WA solution SVT still regulates the entire house as a single system and as such it may even be able to patterns it can regulate towards. In the WA solution we can replace SVT by any other means of controlling the boiler, SVT just happened to be easiest to use. If I would install a modulating boiler controller (not a thermostat) I could further reduce the energy usage. Another pro of WA in my opinion. I am planning on building such a modulating controller using an opentherm interface to my boiler. This controller would have a setpoint plus a temperature input (as opposed to a thermostat which has a built-in temperature sensor), to regulate the boiler's burning. But that is a next project after this one.

8 - The WA solution allows for a semi-zone-heating system. I don't have valves in all of my rooms yet. For those rooms that I don't have a valve yet, I do have a temperature sensor. I can use this sensor, setting the valve opening value for this room to 100% fixed and this sensor will still help regulate the temperature in the rest of the (unregulated) rooms.
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by rrozema »

An update on my zone heating script. I'm experimenting with only steering based on the zones I do have, the other rooms are of course still heated as normally, but the system is controlled by the both rooms that have a valve installed (kitchen and bathroom) only.

One error was that the boiler wasn't shut down when not a single valve is open. This is now fixed by adding an anti-freeze mode: if no zone requests heat, SVT is set to 5 degrees and given the lowest temperature from any temperature sensor in the house. If no zone requests heat, the system will never let any room in the house go below 5 degrees to prevent freezing of the pipes or radiators.

Code: Select all

local KEUKEN_VALVE_NAME = "Keuken: Radiator: Valve Opening"
local KEUKEN_SENSOR_NAME = "Keuken: Radiator: Air Temperature"
local KEUKEN_SENSOR2_NAME = "Keuken: TempHum"
local KEUKEN_SETPOINT_NAME = "Keuken: Radiator: Heat"

local BADKAMER_VALVE_NAME = "Badkamer: Radiator: Valve Opening"
local BADKAMER_SENSOR_NAME = "Badkamer: Radiator: Air Temperature"
local BADKAMER_SENSOR2_NAME = "Badkamer: TempHum"
local BADKAMER_SETPOINT_NAME = "Badkamer: Radiator: Heat"

local WOONKAMER_SENSOR_NAME = "Woonkamer: TempHum"
local WOONKAMER_SETPOINT_NAME = "Woonkamer: Verwarming"


local VERWARMING_PAUSE_NAME = "Verwarming Pause"
local VERWARMING_SETPOINT_NAME = "Verwarming"
local VERWARMING_TEMPERATURE_NAME = "Verwarming: Temperature"


return {
	on = {
	    devices = {
	        BADKAMER_VALVE_NAME,
	        KEUKEN_VALVE_NAME,
	        
	        BADKAMER_SENSOR_NAME,
	        BADKAMER_SENSOR2_NAME,
	        KEUKEN_SENSOR_NAME,
	        KEUKEN_SENSOR2_NAME,
	        WOONKAMER_SENSOR_NAME,
	        
	        BADKAMER_SETPOINT_NAME,
	        KEUKEN_SETPOINT_NAME,
	        WOONKAMER_SETPOINT_NAME
	    }
	},
	data = {
	    minimum_opening = { initial = {} },
	    maximum_opening = { initial = {} }
	},
--	logging = {
--        level = domoticz.LOG_ERROR,
--        marker = "Richard"
--    },
    execute = function(domoticz, device)

--        domoticz.utils.log('Device = ' .. device.name)
	    
        if (true == device.isDevice) then
            domoticz.log('Device ' .. device.name, domoticz.LOG_ERROR)
            
            local keuken_valve = domoticz.devices(KEUKEN_VALVE_NAME)
            local keuken_sensor = domoticz.devices(KEUKEN_SENSOR_NAME)
            local keuken_sensor2 = domoticz.devices(KEUKEN_SENSOR2_NAME)
            local keuken_setpoint = domoticz.devices(KEUKEN_SETPOINT_NAME)
            
            local badkamer_valve = domoticz.devices(BADKAMER_VALVE_NAME)
            local badkamer_sensor = domoticz.devices(BADKAMER_SENSOR_NAME)
            local badkamer_sensor2 = domoticz.devices(BADKAMER_SENSOR2_NAME)
            local badkamer_setpoint = domoticz.devices(BADKAMER_SETPOINT_NAME)

            local woonkamer_sensor = domoticz.devices(WOONKAMER_SENSOR_NAME)
            local woonkamer_setpoint = domoticz.devices(WOONKAMER_SETPOINT_NAME)
            
            local verwarming_pause = domoticz.devices(VERWARMING_PAUSE_NAME)
            local verwarming_setpoint = domoticz.devices(VERWARMING_SETPOINT_NAME)
            local verwarming_temperature = domoticz.devices(VERWARMING_TEMPERATURE_NAME)
            
            -- Depending on the way the valve got installed and the valve foot on the radiator, the valve may 
            -- close well above 0% and thus never reach that 0%. I want a range of 0 to 100% though to calculate 
            -- a proper weighted average temperature. So for this reason I keep track of the lowest opening 
            -- level ever reached for each valve, so I can do a correction on the opening level further
            -- below. The same may be true for the maximum level, but I'm going to ignore that for now.
            --
            -- Instruction to get the minimum value set fastest: Put the setpoint for each valve at an extremely 
            -- low temperature -i.e. well below the current room temperature- and leave it there for a few minutes so
            -- that the valve will close. The script will now 'learn' the lowest opening value this valve can reach.
            if (domoticz.data.minimum_opening[KEUKEN_VALVE_NAME] == nil or keuken_valve.level < domoticz.data.minimum_opening[KEUKEN_VALVE_NAME]) then
                domoticz.data.minimum_opening[KEUKEN_VALVE_NAME] = keuken_valve.level
            end
            if (domoticz.data.maximum_opening[KEUKEN_VALVE_NAME] == nil or keuken_valve.level > domoticz.data.maximum_opening[KEUKEN_VALVE_NAME]) then
                domoticz.data.maximum_opening[KEUKEN_VALVE_NAME] = keuken_valve.level
            end

            if (domoticz.data.minimum_opening[BADKAMER_VALVE_NAME] == nil or badkamer_valve.level < domoticz.data.minimum_opening[BADKAMER_VALVE_NAME]) then
                domoticz.data.minimum_opening[BADKAMER_VALVE_NAME] = badkamer_valve.level
            end
            if (domoticz.data.maximum_opening[BADKAMER_VALVE_NAME] == nil or badkamer_valve.level > domoticz.data.maximum_opening[BADKAMER_VALVE_NAME]) then
                domoticz.data.maximum_opening[BADKAMER_VALVE_NAME] = badkamer_valve.level
            end

            -- Determine the corrected weight-factor for each valve. 
            -- Remark: In the end the correction wasn't needed for
            -- any of my valves; the SpiritZ does a great job 
            -- calibrating the valve opening to be from 0% to 100%.
            local weight = {}
            
            if (keuken_valve.level < domoticz.data.minimum_opening[KEUKEN_VALVE_NAME] or domoticz.data.maximum_opening[KEUKEN_VALVE_NAME] <= domoticz.data.minimum_opening[KEUKEN_VALVE_NAME]) then
                weight[KEUKEN_VALVE_NAME] = 0.0
            else
                weight[KEUKEN_VALVE_NAME] = 100.0 * ((keuken_valve.level - domoticz.data.minimum_opening[KEUKEN_VALVE_NAME]) / (domoticz.data.maximum_opening[KEUKEN_VALVE_NAME] - domoticz.data.minimum_opening[KEUKEN_VALVE_NAME]))
            end
            
            if (badkamer_valve.level < domoticz.data.minimum_opening[BADKAMER_VALVE_NAME] or domoticz.data.maximum_opening[BADKAMER_VALVE_NAME] <= domoticz.data.minimum_opening[BADKAMER_VALVE_NAME]) then
                weight[BADKAMER_VALVE_NAME] = 0.0
            else
                weight[BADKAMER_VALVE_NAME] = 100.0 * ((badkamer_valve.level - domoticz.data.minimum_opening[BADKAMER_VALVE_NAME]) / (domoticz.data.maximum_opening[BADKAMER_VALVE_NAME] - domoticz.data.minimum_opening[BADKAMER_VALVE_NAME]))
            end

            local setpoint_calculated = nil
            local weighted_temperature = nil

            -- 1: Calculate the sum of all weights.
            local weight_total = (
--                    100 
                    weight[KEUKEN_VALVE_NAME] 
--                  + weight[KEUKEN_VALVE_NAME] 
                    + weight[BADKAMER_VALVE_NAME] 
--                    + weight[BADKAMER_VALVE_NAME]
                )

            -- If at least one valve is requesting heat...
            if (weight_total > 0) then
                -- .. determine the setpoint by finding the highest setpoint for
                -- which the weight is non-zero.
                local setpoints = {
    --                    woonkamer_setpoint.setPoint
                    }    -- woonkamer doesn't have a valve, it is always fully open (i.e. at 100%).
                if weight[KEUKEN_VALVE_NAME] > 0 then
                    table.insert( setpoints, keuken_setpoint.setPoint)
                end
                if weight[BADKAMER_VALVE_NAME] > 0 then
                    table.insert( setpoints, badkamer_setpoint.setPoint)
                end

                for i, v in ipairs( setpoints ) do
                    if (setpoint_calculated == nil or v > setpoint_calculated) then
                       setpoint_calculated = v
                    end
                end

                -- Determine the weighted average of all temperature sensors:
                -- 2: the sum of the temperatures times their weight,
                local temperature_total = 
    --                100 * woonkamer_sensor.temperature 
                    weight[KEUKEN_VALVE_NAME] * keuken_sensor.temperature 
    --                + weight[KEUKEN_VALVE_NAME] * keuken_sensor2.temperature
                    + weight[BADKAMER_VALVE_NAME] * badkamer_sensor.temperature
    --                + weight[BADKAMER_VALVE_NAME] * badkamer_sensor2.temperature
    
                -- 3: Divide the total temperature by the total weight, and 
                -- round the result to 1 decimal.
                weighted_temperature = math.floor((temperature_total / weight_total) * 10 + 0.5) / 10
            else
                -- Anti freeze: if not a single valve is open, set the setpoint to 
                -- 5 degrees and use the lowest temperature reading from any of the 
                -- sensors available for the current temperature. This should not 
                -- cost much energy, but does prevent the system from freezing when 
                -- left inactive for a long time.
                setpoint_calculated = 5
                weighted_temperature = math.min(
                        woonkamer_sensor.temperature, 
                        keuken_sensor.temperature, 
                        keuken_sensor2.temperature, 
                        badkamer_sensor.temperature, 
                        badkamer_sensor2.temperature
                    )
            end

            domoticz.log('Calculated setpoint: ' .. tostring(setpoint_calculated) .. ', Weighted temperature: ' .. tostring(weighted_temperature) .. ', weights: bk: ' .. weight[BADKAMER_VALVE_NAME] .. ', k: ' .. weight[KEUKEN_VALVE_NAME] .. '.', domoticz.LOG_ERROR)
            
            if (verwarming_setpoint.setPoint ~= setpoint_calculated) then
                domoticz.log('Updating setpoint: ' .. tostring(setpoint_calculated).. '.', domoticz.LOG_ERROR)
                verwarming_setpoint.updateSetPoint(setpoint_calculated)
            end
            
            if (verwarming_temperature.temperature ~= weighted_temperature) then
                domoticz.log('Updating temparature: ' .. tostring(weighted_temperature).. '.', domoticz.LOG_ERROR)
                verwarming_temperature.updateTemperature(weighted_temperature)
            end
        end
	end
}
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by rrozema »

Another update. It was getting hot in here (Pun intended). The living room (woonkamer), was set to a weight of 100% fixed. That was not a good idea. If one of the regulated rooms was set to a temperature higher than the (unregulated) living room, the setpoint in those other rooms was almost impossible to reach because it was always an average in which the living room weighed much more than the regulated room. So to fix this I created a virtual weight for the living room too: based on the difference between the setpoint and the actual temperature measured in the living room. If temp is over setpoint, valve is at 0%, if it's 5 degrees or more under the setpoint, the valve is at 100% anything in between is linearly calculated. The living room will still overheat when one or more regulated rooms are requesting hot water when the living room is already at or above its setpoint. But at least the boiler will now switch off when the regulated room reaches its setpoint.

Here's the new script:

Code: Select all

local KEUKEN_VALVE_NAME = "Keuken: Radiator: Valve Opening"             -- Valve opening percentage for the kitchen.
                                                                        -- (Eurotronic SpiritZ "Valve opening" device)
local KEUKEN_SENSOR_NAME = "Keuken: Radiator: Air Temperature"          -- Temperature sensor for the kitchen.
                                                                        -- (Eurotronic SpiritZ "Äir temperature" device)
local KEUKEN_SENSOR2_NAME = "Keuken: TempHum"                           -- An additional temperature sensor in the bathroom.
                                                                        -- (Aeotec Multi sensor 6 "TempHum" device)
local KEUKEN_SETPOINT_NAME = "Keuken: Verwarming"                       -- The setPoint device for the kitchen.
                                                                        -- (Eurotronic SpiritZ "heat" device)


local BADKAMER_VALVE_NAME = "Badkamer: Radiator: Valve Opening"         -- Valve opening percentage for the bathroom.
                                                                        -- (Eurotronic SpiritZ "Valve opening" device)
local BADKAMER_SENSOR_NAME = "Badkamer: Radiator: Air Temperature"      -- Temperature sensor for the bathroom.
                                                                        -- (Eurotronic SpiritZ "Äir temperature" device)
local BADKAMER_SENSOR2_NAME = "Badkamer: TempHum"                       -- An additional temperature sensor in the bathroom.
                                                                        -- (Everspring ST814 "TempHum" device)
local BADKAMER_SETPOINT_NAME = "Badkamer: Verwarming"                   -- The setPoint device for the bathroom 
                                                                        -- (Eurotronic SpiritZ "heat" device)


local WOONKAMER_VALVE_NAME = "Woonkamer: Radiator: Valve Opening"       -- Not a real device name, only used internally.
local WOONKAMER_SENSOR_NAME = "Woonkamer: TempHum"                      -- A temperature sensor in the living room 
                                                                        -- (Aeotec Multi sensor 6).
local WOONKAMER_SETPOINT_NAME = "Woonkamer: Verwarming"                 -- A dummy temperature setPoint device, used to set 
                                                                        -- the desired temperature in the living room.


local VERWARMING_SETPOINT_NAME = "Verwarming"                           -- SVT's normal setpoint device.
local VERWARMING_TEMPERATURE_NAME = "Verwarming: Temperature"           -- A dummy temperature sensor. This is the device
                                                                        -- that SVT reads as it's temperature sensor.
--local VERWARMING_PAUSE_NAME = "Verwarming Pause"                      -- SVT's Pause device.
                                                                        -- I don't want to use SVT's pause mode in this script,
                                                                        -- as that is already controlled by door sensors in my 
                                                                        -- house: if an outside door is open SVT is paused. My 
                                                                        -- wife likes to air the livingroom sometimes and most
                                                                        -- of the times she forgets to turn off the heating,
                                                                        -- so I do that for her using SVT's pause mode.


return {
	on = {
	    devices = {
	        BADKAMER_VALVE_NAME,
	        KEUKEN_VALVE_NAME,
	        
	        BADKAMER_SENSOR_NAME,
	        BADKAMER_SENSOR2_NAME,
	        KEUKEN_SENSOR_NAME,
	        KEUKEN_SENSOR2_NAME,
	        WOONKAMER_SENSOR_NAME,
	        
	        BADKAMER_SETPOINT_NAME,
	        KEUKEN_SETPOINT_NAME,
	        WOONKAMER_SETPOINT_NAME
	    }
	},
	data = {
	    minimum_opening = { initial = {} },
	    maximum_opening = { initial = {} }
	},
--	logging = {
--        level = domoticz.LOG_ERROR,
--        marker = "Richard"
--    },
    execute = function(domoticz, device)

--        domoticz.utils.log('Device = ' .. device.name)
	    
        if (true == device.isDevice) then
            domoticz.log('Device ' .. device.name, domoticz.LOG_ERROR)
            
            local keuken_valve = domoticz.devices(KEUKEN_VALVE_NAME)
            local keuken_sensor = domoticz.devices(KEUKEN_SENSOR_NAME)
            local keuken_sensor2 = domoticz.devices(KEUKEN_SENSOR2_NAME)
            local keuken_setpoint = domoticz.devices(KEUKEN_SETPOINT_NAME)
            
            local badkamer_valve = domoticz.devices(BADKAMER_VALVE_NAME)
            local badkamer_sensor = domoticz.devices(BADKAMER_SENSOR_NAME)
            local badkamer_sensor2 = domoticz.devices(BADKAMER_SENSOR2_NAME)
            local badkamer_setpoint = domoticz.devices(BADKAMER_SETPOINT_NAME)

            local woonkamer_sensor = domoticz.devices(WOONKAMER_SENSOR_NAME)
            local woonkamer_setpoint = domoticz.devices(WOONKAMER_SETPOINT_NAME)
            
            local verwarming_pause = domoticz.devices(VERWARMING_PAUSE_NAME)
            local verwarming_setpoint = domoticz.devices(VERWARMING_SETPOINT_NAME)
            local verwarming_temperature = domoticz.devices(VERWARMING_TEMPERATURE_NAME)
            
            -- Depending on the way the valve got installed and the valve foot on the radiator, the valve may 
            -- close well above 0% and thus never reach that 0%. I want a range of 0 to 100% though to calculate 
            -- a proper weighted average temperature. So for this reason I keep track of the lowest opening 
            -- level ever reached for each valve, so I can do a correction on the opening level further
            -- below. The same may be true for the maximum level, but I'm going to ignore that for now.
            --
            -- Instruction to get the minimum value set fastest: Put the setpoint for each valve at an extremely 
            -- low temperature -i.e. well below the current room temperature- and leave it there for a few minutes so
            -- that the valve will close. The script will now 'learn' the lowest opening value this valve can reach.
            if (domoticz.data.minimum_opening[KEUKEN_VALVE_NAME] == nil or keuken_valve.level < domoticz.data.minimum_opening[KEUKEN_VALVE_NAME]) then
                domoticz.data.minimum_opening[KEUKEN_VALVE_NAME] = keuken_valve.level
            end
            if (domoticz.data.maximum_opening[KEUKEN_VALVE_NAME] == nil or keuken_valve.level > domoticz.data.maximum_opening[KEUKEN_VALVE_NAME]) then
                domoticz.data.maximum_opening[KEUKEN_VALVE_NAME] = keuken_valve.level
            end

            if (domoticz.data.minimum_opening[BADKAMER_VALVE_NAME] == nil or badkamer_valve.level < domoticz.data.minimum_opening[BADKAMER_VALVE_NAME]) then
                domoticz.data.minimum_opening[BADKAMER_VALVE_NAME] = badkamer_valve.level
            end
            if (domoticz.data.maximum_opening[BADKAMER_VALVE_NAME] == nil or badkamer_valve.level > domoticz.data.maximum_opening[BADKAMER_VALVE_NAME]) then
                domoticz.data.maximum_opening[BADKAMER_VALVE_NAME] = badkamer_valve.level
            end

            local weight = {}

            -- Woonkamer doesn't have a valve (yet). To make sure it's temperature
            -- sensor gets a proper weight like those of the regulated rooms I
            -- calculate a virtual valve opening value from 100% to 0% linear
            -- fpr when the temperature is from 2.5 degrees below the setPoint
            -- to the setpoint value.
            if woonkamer_sensor.temperature >= woonkamer_setpoint.setPoint then
                weight[WOONKAMER_VALVE_NAME] = 0.0
            else
                local difference = woonkamer_setpoint.setPoint - woonkamer_sensor.temperature
                
                if difference >= 2.5 then
                    weight[WOONKAMER_VALVE_NAME] = 100
                else
                    weight[WOONKAMER_VALVE_NAME] = difference * 40
                end
            end
            
            -- Determine the corrected weight-factor for each valve. 
            -- Remark: In the end the correction wasn't needed for
            -- any of my valves; the SpiritZ does a great job 
            -- calibrating the valve opening to be from 0% to 100%.
            if (keuken_valve.level < domoticz.data.minimum_opening[KEUKEN_VALVE_NAME] or domoticz.data.maximum_opening[KEUKEN_VALVE_NAME] <= domoticz.data.minimum_opening[KEUKEN_VALVE_NAME]) then
                weight[KEUKEN_VALVE_NAME] = 0.0
            else
                weight[KEUKEN_VALVE_NAME] = 100.0 * ((keuken_valve.level - domoticz.data.minimum_opening[KEUKEN_VALVE_NAME]) / (domoticz.data.maximum_opening[KEUKEN_VALVE_NAME] - domoticz.data.minimum_opening[KEUKEN_VALVE_NAME]))
            end
            
            if (badkamer_valve.level < domoticz.data.minimum_opening[BADKAMER_VALVE_NAME] or domoticz.data.maximum_opening[BADKAMER_VALVE_NAME] <= domoticz.data.minimum_opening[BADKAMER_VALVE_NAME]) then
                weight[BADKAMER_VALVE_NAME] = 0.0
            else
                weight[BADKAMER_VALVE_NAME] = 100.0 * ((badkamer_valve.level - domoticz.data.minimum_opening[BADKAMER_VALVE_NAME]) / (domoticz.data.maximum_opening[BADKAMER_VALVE_NAME] - domoticz.data.minimum_opening[BADKAMER_VALVE_NAME]))
            end

            local setpoint_calculated = nil
            local weighted_temperature = nil

            -- 1: Calculate the sum of all weights.
            local weight_total = (
                    weight[WOONKAMER_VALVE_NAME]
                    + weight[KEUKEN_VALVE_NAME] 
--                  + weight[KEUKEN_VALVE_NAME] 
                    + weight[BADKAMER_VALVE_NAME] 
--                    + weight[BADKAMER_VALVE_NAME]
                )

            -- If at least one valve is requesting heat...
            if (weight_total > 0.0) then
                -- .. determine the setpoint by finding the highest setpoint for
                -- which the weight is non-zero.
                local setpoints = {}
                if weight[WOONKAMER_VALVE_NAME] > 0.0 then
                    table.insert( setpoints, woonkamer_setpoint.setPoint)
                end
                if weight[KEUKEN_VALVE_NAME] > 0.0 then
                    table.insert( setpoints, keuken_setpoint.setPoint)
                end
                if weight[BADKAMER_VALVE_NAME] > 0.0 then
                    table.insert( setpoints, badkamer_setpoint.setPoint)
                end

                for i, v in ipairs( setpoints ) do
                    if (setpoint_calculated == nil or v > setpoint_calculated) then
                       setpoint_calculated = v
                    end
                end

                -- Determine the weighted average of all temperature sensors:
                -- 2: the sum of the temperatures times their weight,
                local temperature_total = 
                    weight[WOONKAMER_VALVE_NAME] * woonkamer_sensor.temperature
                    + weight[KEUKEN_VALVE_NAME] * keuken_sensor.temperature 
    --                + weight[KEUKEN_VALVE_NAME] * keuken_sensor2.temperature
                    + weight[BADKAMER_VALVE_NAME] * badkamer_sensor.temperature
    --                + weight[BADKAMER_VALVE_NAME] * badkamer_sensor2.temperature
    
                -- 3: Divide the total temperature by the total weight, and
                -- round the result to 1 decimal.
                weighted_temperature = math.floor((temperature_total / weight_total) * 10 + 0.5) / 10
            else
                -- Anti freeze: if not a single valve is open, set the setpoint to 
                -- 5 degrees and use the lowest temperature reading from any of the 
                -- sensors available for the current temperature. This should not 
                -- cost much energy, but does prevent the system from freezing when 
                -- left inactive for a long time.
                setpoint_calculated = 5
                weighted_temperature = math.min(
                        woonkamer_sensor.temperature, 
                        keuken_sensor.temperature, 
                        keuken_sensor2.temperature, 
                        badkamer_sensor.temperature, 
                        badkamer_sensor2.temperature
                    )
            end

            domoticz.log('Calculated setpoint: ' .. tostring(setpoint_calculated) .. ', Weighted temperature: ' .. tostring(weighted_temperature) .. ', weights: wk: ' .. tostring(weight[WOONKAMER_VALVE_NAME]) .. ', bk: ' .. tostring(weight[BADKAMER_VALVE_NAME]) .. ', k: ' .. tostring(weight[KEUKEN_VALVE_NAME]) .. '.', domoticz.LOG_ERROR)
            
            if (verwarming_setpoint.setPoint ~= setpoint_calculated) then
                domoticz.log('Updating setpoint: ' .. tostring(setpoint_calculated).. '.', domoticz.LOG_ERROR)
                verwarming_setpoint.updateSetPoint(setpoint_calculated)
            end
            
            if (math.floor(verwarming_temperature.temperature * 10 + 0.5) / 10 ~= math.floor(weighted_temperature * 10 + 0.5) / 10) then
                domoticz.log('Updating temperature: ' .. tostring(weighted_temperature).. '.', domoticz.LOG_ERROR)
                verwarming_temperature.updateTemperature(weighted_temperature)
            end
        end
	end
}
edit: No functional changes. Added some comments, changed names for some devices and changed the temperature range for the living room's valvel percentage calculation to 2.5 degrees instead of 5 degrees.
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by rrozema »

Sadly, the WAF (Wife Approval Factor) for my little project dropped below 0: The regulated rooms were perfectly regulated, the rest of the rooms however also kept getting hot water fed whenever one of the regulated rooms requested heat. At some point both the bathroom and the kitchen happened to request heat almost alternating: soon after the kitchen was at its setpoint, the bathroom needed more heat and vice versa. Result was that the living room heated up far too high (almost 25 degrees) and now I'm "not allowed" to play around with the heating anymore until I can make sure this won't happen again.

The obvious solution is of course to install more SpiritZ's in all rooms, but I don't have valves installed in those rooms yet, so that has to wait. Until that is done I've made SVT read the living room sensor only for the moment, so that the living room is again the only room requesting heat. Paused for the moment....
iganin
Posts: 47
Joined: Tuesday 28 October 2014 17:55
Target OS: NAS (Synology & others)
Domoticz version: 2020.2
Location: Aalter, Belgium
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by iganin »

Do not give up!!!

BTW, In my setup, I have got SVT per a room which control the zone valve.
There there is a scrip:
1. If one of the zone valves is open, then start circulation pump and heat the boiler water.
2. If all zone valves are closed, stop the pump and heating the boiler water.
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by rrozema »

iganin wrote: Thursday 26 November 2020 16:40 Do not give up!!!

BTW, In my setup, I have got SVT per a room which control the zone valve.
There there is a scrip:
1. If one of the zone valves is open, then start circulation pump and heat the boiler water.
2. If all zone valves are closed, stop the pump and heating the boiler water.
Do you use spiritz devices in all of your rooms? And if so, do you use them in manufacturer specific mode or in normal mode? I think you're using manufacturer specific mode, if so I'd be interested to know about your experiences with that. Eease of use, battery usage, comfort, reliability, etc. Do my arguments on the pro and cons I listed above make any sense?

And how exactly do you control the valves using svt? svt only has the ability to set a switch (or a set of switches) from On to Off and vice versa, doesn't it? So how do you make your SVT's control the valves -which are to be set from 0% to 100%- and the boiler - which is shared between all svt's-?
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by rrozema »

Anyone who knows how to initiate a re-include of the eurotronic spirit Z? So I want to start the inclusion process in the spirit Z without first excluding the device from domoticz, as to get it re-included with the same node ID.

I've restarted my domoticz too soon (i.e. before querying all nodes was completed) and now I have 2 nodes that show 0x0000 for the ID and Type in the z-wave nodes screen.
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by rrozema »

I haven't updated you all for a long time, so here's an update:

I'm trying to get the ZWave thermostat mode working in Domoticz, but I run into a lot of bugs in this area. Some are due to code in Domoticz, but the root cause seems to lie in the OpenZWave library which presents a very inconstistent and incomplete API for reading and setting a.o. the Thermostat Mode. Also affected are: Thermostat Fan Mode, Thermostat Operating State and Thermostat Fan Operating State, but these aren't implemented by the SpiritZ i use. Yet if I get the mode to work correctly, I think the others should be easy to fix too.

The problems with the Thermostat Mode center around the OpenZWave trying to be smart about the mode. It tries to validate the mode against the list of supported values reported by the z-wave device. My Spirit Z's support modes Off, Heat, Heat Econ, Full Power and Manufacturer Specific. The values for these modes are 0, 1, 5, 11 and 31. In many places however these values are mixed up with the index into the array of supported modes, i.e. 0, 1, 2, 3, 4. In a recent beta gizmocuz included a fix I found, so the mode can now be properly set again, provided you use a recent beta build. When Domoticz gets notified however by the device about a changed value, OpenZwave tries to validate the incoming Value as if it was an index into that array, resulting in only modes Off and Heat (0 and 1) to work correctly. For the other modes the value does not match the index, so openzwave rejectes these values and thus the notification is never sent to Domoticz.

In Domoticz a similar issue exists in the webserver code: the list of options shown in the thermostat mode device's edit screen is filled with the list of modes supported by the device. Initially for each mode the proper combination of description + value is included. When the screen gets rebuilt however, that list is rebuilt with an incorrect combination of description + index instead. Plus after the edit screen gets posted back to make the mode you selected the new mode, it is again validated as if the value in the list were an index. And yet, the code getting called afterwards expects a value, NOT the index....

I am trying to fix this, but the methods OpenZWave provides for a ValueList (the internal structure to store the supported modes and other device type's options in) are incomplete and inconsistent: it provides methods that allow the device to be set using a value, but the list of option items is only available using the index. Plus in some places the parameter and method names suggest they work on/expect the value, but instead they use that parameter as if it is an index. Then on top of that, OpenZWave has an internal additional layer of code trying to shield the clients of the library (like Domoticz) from having to deal with locking and other concurrency issues of the zwave library: the manager. This manager however provides only a subset of the actual functionality available for ValueList items, so some information that is available inside OpenZWave in the ValueList structures, can not be retrieved or set from within Domoticz easily.

And I was just told that there is little chance of getting this fixed soon, as the main developer of the OpenZwave library is not active on the project for the moment. So I think I'll have to wait some more before I can finish my zone-heating script...
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by rrozema »

I do have a few observations that I want to put in here, so I don't forget them, plus some of you might have ideas on them, so please do respond if you do.
  • svt tries to apply 'gentle nudges' to keep the room temperature setpoint close to the setpoint, based on previous behavior of the system.
  • svt tries to learn the rate in which the room heats up and cools down.
  • my script will change the setpoint very often, depending on which room requests heat.
  • which rooms will get hot water varies all the time, so the rate at which the rooms heat up after some 'burst' of heat is applied varies a lot too.
This is why I think I'm not going to use svt any more, but instead just switch the boiler on if at least one room requests heat and off when all rooms are at their setpoint, as indicated by the spirits' valve opening. My boiler has an internal thermostat that will keep the circulating water at the most optimal temperature anyway using the temperature of the water returned from the radiators. I installed a bypass in the system just for this situation where all of the valves could be (nearly) closed, so the boiler should not get damaged and it will reduce the intensity of the heat applied automatically when the water returned is hardly cooled because it doesn't go through a radiator, so this shouldn't waste much energy.
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by rrozema »

I'm still waiting for a fix for not being able to properly set and read the Eurotronic SpiritZ's thermostat mode. Only 0 - Off and 1 - Heat are properly set, the other modes are rejected by the device most of the times because the value Domoticz tries to set is the position of the option in the list, whereas the device expects to receive values from a list, predefined by the zwave protocol and the Eurotronic SpiritZ supports modes that have non-contiguous values in said list.

I have tried before to write a fix myself, but I'm lacking the knowledge and experience in programming to make a real fix. As another few months have passed and no more development seems to take place on (domoticz's support of) OpenZwave, as another non-mature z-wave initiative is pushed forward to replace it. I'm very temped to abandon this little scripting project and do a serious investment in buying plugwise hardware to achieve the same effect: It is very cold again in my work-from-home workplace, because the ground floor is warm enough since my wife is backing and cooking in the kitchen...
User avatar
kiddigital
Posts: 435
Joined: Thursday 10 August 2017 6:52
Target OS: Raspberry Pi / ODroid
Domoticz version: Beta
Location: Netherlands
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by kiddigital »

Similar problem and same devices using the integrated OpenZWave.

What I do not ‘see’ (yet) is how to force the central heating to keep heating when the ‘base’ reports it is ok.

Not sure (lack of understanding) how the Spirit heating modes are involved.
One RPi with Domoticz, RFX433e, aeon labs z-wave plus stick GEN5, ha-bridge 5.4.0 for Alexa, Philips Hue Bridge, Pimoroni Automation Hat
One RPi with Pi foundation standard touch screen to display Dashticz
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by rrozema »

kiddigital wrote: Thursday 27 January 2022 15:59 What I do not ‘see’ (yet) is how to force the central heating to keep heating when the ‘base’ reports it is ok.
For that I have replaced the thermostat by a wifi relay. So I can now have Domoticz switch the heater on and off independent of the thermostat.
Not sure (lack of understanding) how the Spirit heating modes are involved.
To determine if the heater needs to switch on I need to know if any of the zones requires heat. To do that I can read the valve opening, but that is useless if the mode is anything else than 1-heat or 5-economy: If the mode is 0-off, the valve opening read from the device is the last known valve opening before the mode was set to off. When it is one of the other modes likewise the valve opening needs to be interpreted.

The requested temperature is also dependent on the mode: mode 1 -> normal setpoint, 5 -> economy setpoint, 0-> no heat requested(or anti freeze), 11-forced 100% power requested no known setpoint, 32-manufacturer specific -> heat requested if valve opening > 0 with no known setpoint.
User avatar
kiddigital
Posts: 435
Joined: Thursday 10 August 2017 6:52
Target OS: Raspberry Pi / ODroid
Domoticz version: Beta
Location: Netherlands
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by kiddigital »

So you have a on/off heater and not a modulating one?

Why read the valve opening and not the reported temperature? Thats tells you the difference between the Set and actual temperature. So if heating for the zone is needed or not.

Is there a reason to switch heating modes? In my case I only use Heat and no need to turn it to off. Once the temperature is reached the valve will be closed anyway.

Maybe I am missing something.

But I took a quick look at the code and currently the Domoticz fork of the OpenZwave code does NOT report the correct mode index numbers. It just gives each mode an increased counter.

Not sure if it later on maps it back or not, but I guess not. Problem of the current OpenZWave code. Haven’t checked if it has been updated upstream already.
One RPi with Domoticz, RFX433e, aeon labs z-wave plus stick GEN5, ha-bridge 5.4.0 for Alexa, Philips Hue Bridge, Pimoroni Automation Hat
One RPi with Pi foundation standard touch screen to display Dashticz
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: DIY (semi) zone heating using Eurotronic SpiritZ

Post by rrozema »

I want the device to be the way to control the heat in the room, so everyone can control their own zone either from Domoticz, through Google assistant (voice) or manually via the controls on the device. One of my kids has his heating set to off pretty most all of the time as he's almost never cold. And I like to switch my radiator off as well when I'm not working from home. Off mode is automatically set when you keep pressing the minus button on the device under 8 degrees: The valve opening still has the last value, only the mode will go to off. When in off mode, also the temperature in that zone is not important any more.

I have a heater that can be controlled via on-off or opentherm. I do have all the parts to build an opentherm interface too, but I just never got to building it yet (still some things software wise that are not clear to me) it's mostly time that's the limiting factor. I was planning on having the heater regulate it's power based on the number of zones requesting heat and the highest active setpoint, I just never got to it because of the mode issues.

I have been modifying openzwave and Domoticz to make the mode work correctly, but never got it completely working. I'm moderate competent in c(++) (very rusty, as it's been over 25 years that I last programmed in C) but to fix this issue I need to make changes in openzwave internally, in the openzwave API and then in Domoticz' webserver coding. It's too big a project for my limited skills at the moment.
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest