So far in two days I have got the valves running but manually setting the setpoint for day/night. I have wired an 8 relay low switching opto board to the GPIO. I have a 1wire DS18B20 connected.
I will have a DS18B20 in each room for heat control and PIR devices hardwired into GPIO which will act as room occupied sensors for setpoints.
I found a script yesterday for heating control but now I can't find the post again, it looks at multiple zones with temp sensors and setpoints for each then switching a boiler output with hysteresis. This is running and I managed to write a script to invert the output for my GPIO relay, which I know could easily live in the heating script.
Code: Select all
-- boiler on/off GPIO device script
commandArray = {}
if devicechanged['Boiler']=='On' then
commandArray['BoilerGPIO']='Off'
end
if devicechanged['Boiler']=='Off' then
commandArray['BoilerGPIO']='On'
end
return commandArray
My problem is that blockly won't do the math for temperature setpoints.
I am posting this script because I can't find the owner now (sorry). I know that this script could set the valve setpoints as it loops, when it sees that heat is needed in that zone it can set a rad valve to 25 and when heat is not needed it can set a rad valve to 15. This will work well because the StellaZ are stuck in comfort mode and a big change should create open and close events rather than feathering the rad valve.
Please help me with this, I really can't get my head around strings to numbers.
Code: Select all
--[[
Heating Coordinator Script
==========================
This script is used to manage the firing of a boiler to support multiple zone requirements using temperature sensor readings and thermostat setpoints in each.
User variable requirements
--------------------------
Name: "HeatingCoordinator_ThermostatNamePrefix"
Type: "String"
Description: Defines the thermostat device name prefix, a device with a name made from this prefix followed by the zone should be present
Example: "Therm_"
Name: "HeatingCoordinator_TempSensorNamePrefix"
Type: "String"
Description: Defines the temp sensor device name prefix, a device with a name made from this prefix followed by the zone should be present
Example: "Temp_"
Name: "HeatingCoordinator_BoilerReceiverSwitchName"
Type: "String"
Description: Defines the boiler receiver device naming
Example: "Boiler"
Name: "HeatingCoordinator_Zones"
Type: "String"
Description: Defines the zone names in a comma delimited list with no spaces
Example: "LivingRoom,Kitchen,BedroomFrontLeft,BedroomFrontRight,BedroomBackLeft"
Name: "HeatingCoordinator_BoilerStateChangeWaitTime"
Type: "Integer"
Description: Defines the length of time in seconds before a boiler state change is allowed after a previous update
Example: 300
Name: "HeatingCoordinator_BoilerAliveTime"
Type: "Integer"
Description: Defines the length of time in seconds since a previous boiler state change before a keep alive update is required. Some receivers will switch off automatically after a given time if not told to stay on, this is normally done by a room thermostat but without one this is required.
Example: 1800
Name: "HeatingCoordinator_TempHysterisis"
Type: "Float"
Description: Defines the amount of temperature allowance either side of the on / off heating states to stop constant switching
Example: 0.5
Name: "HeatingCoordinator_LogActivity"
Type: "Integer"
Description: Defines if all activity is logged ot nor, 1 = yes and 0 = No. Activity relating to state changes is always logged however.
Example: 1
]]
commandArray = {}
function splitString(str, delim, maxNb)
-- Eliminate bad cases...
if string.find(str, delim) == nil then
return { str }
end
if maxNb == nil or maxNb < 1 then
maxNb = 0 -- No limit
end
local result = {}
local pat = "(.-)" .. delim .. "()"
local nb = 0
local lastPos
for part, pos in string.gmatch(str, pat) do
nb = nb + 1
result[nb] = part
lastPos = pos
if nb == maxNb then break end
end
-- Handle the last field
if nb ~= maxNb then
result[nb + 1] = string.sub(str, lastPos)
end
return result
end
function getLastUpdatedTime(deviceName)
lastUpdatedString = otherdevices_lastupdate[deviceName]
year = string.sub(lastUpdatedString, 1, 4)
month = string.sub(lastUpdatedString, 6, 7)
day = string.sub(lastUpdatedString, 9, 10)
hour = string.sub(lastUpdatedString, 12, 13)
minutes = string.sub(lastUpdatedString, 15, 16)
seconds = string.sub(lastUpdatedString, 18, 19)
return os.time{year=year, month=month, day=day, hour=hour, min=minutes, sec=seconds}
end
function logtext(text, override)
if logActivity == 1 then
print("(HeatingCoordinator) "..text)
end
end
function getZoneHeatNeeded(zoneName)
local zoneHeatNeeded = "NotApplicable"
local zoneSetPointMax = -999
local zoneTempMin = 999
for deviceName, deviceValue in pairs(otherdevices) do
if string.find(deviceName, thermostatNamePrefix..zoneName) ~= nil then
-- device value needs refetching via svalues
deviceValue = otherdevices_svalues[deviceName]
-- if zone thermostat has more than just one value get the first one
deviceValueDelimited = string.find(deviceValue, ";")
if deviceValueDelimited == nil then
deviceValue = tonumber(deviceValue)
else
deviceValue = tonumber(string.sub(deviceValue, 0, deviceValueDelimited - 1))
end
-- determine the max setpoint for the zone
if zoneSetPointMax < deviceValue then
zoneSetPointMax = deviceValue
end
end
if string.find(deviceName, tempSensorNamePrefix..zoneName) ~= nil then
-- device value needs refetching via svalues
deviceValue = otherdevices_svalues[deviceName]
-- if zone temp has more than just one value get the first one
deviceValueDelimited = string.find(deviceValue, ";")
if deviceValueDelimited == nil then
deviceValue = tonumber(deviceValue)
else
deviceValue = tonumber(string.sub(deviceValue, 0, deviceValueDelimited - 1))
end
-- determine the min temp for the zone
if zoneTempMin > deviceValue then
zoneTempMin = deviceValue
end
end
end
-- if nothing valid for zone readings assume not applicable
if (zoneSetPointMax == -999 or zoneTempMin == 999) then
zoneHeatNeeded = "NotApplicable"
logtext(string.format(" Zone: Name = '%s', Temp = '%s', SetPoint = '%s', Heat Needed = '%s' (Device Reading Error)", zoneName, zoneTempMin, zoneSetPointMax, zoneHeatNeeded), true)
end
-- determine heating requirements including hysterisis
if (zoneTempMin < (zoneSetPointMax - tempHysteresis)) then
zoneHeatNeeded = "Yes"
elseif (zoneTempMin > (zoneSetPointMax + tempHysteresis)) then
zoneHeatNeeded = "No"
else
zoneHeatNeeded = "NotApplicable"
end
logtext(string.format(" Zone: Name = '%s', Temp = '%s', SetPoint = '%s', Heat Needed = '%s'", zoneName, zoneTempMin, zoneSetPointMax, zoneHeatNeeded))
return zoneHeatNeeded
end
function processZones()
-- loop our zones and see if any needs heat
boilerState = "NotSet"
for heatingZonesIndex = 1, #heatingZones do
-- heat needed to turn on boiler
zoneHeatNeeded = getZoneHeatNeeded(heatingZones[heatingZonesIndex])
if boilerState ~= "On" then
if (zoneHeatNeeded == "Yes") then
boilerState = "On"
elseif (zoneHeatNeeded == "NotApplicable") then
boilerState = "NotApplicable"
elseif (zoneHeatNeeded == "No" and boilerState ~= "NotApplicable") then
boilerState = "Off"
end
end
end
boilerWaitTime = os.difftime(os.time(),getLastUpdatedTime(boilerReceiverSwitchName))
if boilerState == "On" then
if boilerState ~= otherdevices[boilerReceiverSwitchName] then
if boilerWaitTime >= boilerStateChangeWaitTime then
commandArray[boilerReceiverSwitchName] = boilerState
logtext("Boiler State Change: Off -> HeatOn", true)
else
logtext(string.format("Boiler State Change: Off -> HeatOn (in %s secs)",boilerStateChangeWaitTime - boilerWaitTime), true)
end
else
if boilerWaitTime >= boilerAliveTime then
commandArray[boilerReceiverSwitchName] = boilerState
logtext("Boiler State Remains: HeatOn (Keep Alive Update)", true)
else
logtext("Boiler State Remains: HeatOn")
end
end
elseif boilerState == "Off" then
if boilerState ~= otherdevices[boilerReceiverSwitchName] then
if boilerWaitTime >= boilerStateChangeWaitTime then
commandArray[boilerReceiverSwitchName] = boilerState
logtext("Boiler State Change: HeatOn -> Off", true)
else
logtext(string.format("Boiler State Change: HeatOn -> Off (in %s secs)",boilerStateChangeWaitTime - boilerWaitTime), true)
end
else
logtext("Boiler State Remains: Off")
end
elseif boilerState == "NotApplicable" then
if boilerWaitTime >= boilerAliveTime and otherdevices[boilerReceiverSwitchName] == "On" then
commandArray[boilerReceiverSwitchName] = "On"
logtext("Boiler State Remains: HeatOn (Hysterisis / Keep Alive Update)", true)
else
if otherdevices[boilerReceiverSwitchName] == "On" then
logtext("Boiler State Remains: HeatOn (Hysterisis)")
else
logtext("Boiler State Remains: Off (Hysterisis)")
end
end
end
return
end
-- User variable population
thermostatNamePrefix = uservariables["HeatingCoordinator_ThermostatNamePrefix"]
tempSensorNamePrefix = uservariables["HeatingCoordinator_TempSensorNamePrefix"]
boilerReceiverSwitchName = uservariables["HeatingCoordinator_BoilerReceiverSwitchName"]
heatingZones = splitString(uservariables["HeatingCoordinator_Zones"],",")
boilerStateChangeWaitTime = uservariables["HeatingCoordinator_BoilerStateChangeWaitTime"]
boilerAliveTime = uservariables["HeatingCoordinator_BoilerAliveTime"]
tempHysteresis = uservariables["HeatingCoordinator_TempHysterisis"]
logActivity = uservariables["HeatingCoordinator_LogActivity"]
-- Process heating requirements
processZones()
return commandArray
