EQ3 MAX!
Background
EQ3 MAX! A system of inexpensive components including wireless battery operated TRV’s that easily retro-fit to your existing radiators without the need for a plumber, or to drain your system. Once MAX! is installed, it will control each radiators motorised valve individually, creating a zoned heating system with higher efficiency and in turn reduced bills.
All information here is condensed from the topic https://www.domoticz.com/forum/viewtopic.php?f=34&t=841
**** WARNING *****
The Lua version of this plugin is not actively maintained anymore. It may still work, but no guarantees. I strongly suggest to install the Python plugin version, for instructions see https://github.com/mvzut/maxcube-Domoticz-plugin
See also the forum topic https://www.domoticz.com/forum/viewtopic.php?f=34&t=25081
Component Overview
Cube LAN Gateway
The Cube is the gateway between your LAN and the RF comms of the MAX! system. An Ethernet port connects to your network and power is provided via a USB socket. Once attached you can configure the system with the MAX! software. In addition you can monitor the status of your system and control it from your PC or Smartphone / Tablet across the Internet via eQ-3’s server. Local control can be carried out without the need for an Internet connection.
Wireless Wall Thermostat
The wall thermostat can control up to eight MAX! radiator thermostats in any one room and has an internal sensor that measures the temperature in the room and transmits it to the radiator thermostats. The thermostat also reports back to the "Cube".
Radiator Thermostat
The Radiator Thermostat is responsible for regulating your radiators. All initial configuration settings are made with the MAX! Software and different settings can be made for each room. Communication between MAX! components is bi-directional. This ensures that the information sent reaches the recipient. There are three versions available, essentially a Basic, a Standard and a Plus. All units use 2 x AA cells and e-Q3 quote battery life as 2 years with 2 operations per day.
Other Components
Also available (but not yet necessarily Domoticz ready) are
- Wireless Window Sensor that lets the system know when a door or window has been opened allowing the Radiator Thermostats to automatically reduce the temperature to save energy, and increase it again once the window or door has been closed.
- The MAX! Eco switch which allows you to set back the temperature in all your rooms with a single button press when you leave home. When you return you can also set all rooms back to auto mode by pressing the button again.
- There’s also a Plug Adaptor for controlling electric and water heaters but it appears to only be available with a European socket design. It’s unclear whether this could also be used for boiler control.
Hardware requirements
You will need
- Domoticz - A Linux based Domoticz installation as these instructions do not cover a Windows install at this point.
- A Cube LAN Gateway - You will only need one for whole installation. Once set up, additional rooms can be added as and when time and finance allows.
- A Radiator Thermostat - Maximum 8 per room
- (Optional) Window Sensor/Door Sensor
- (Optional)Wireless Wall Thermostat - One per room
- (Optional) Switch - A means of switching your boiler on and off. This is an individual case as everyone has a different heating system and different requirements. So I wouldn't dare to give advice as to which method to use. It is recommended that if you are unsure, seek advice from a Plumber/Electrician for safety.
N.B. I personally have a "Fibaro FGS-222" that controls my heating and hot water as will be seen in the example.
Software Requirements
First you need to update Lua to 5.2. and install some extra lua files.
sudo apt-get install lua5.2 sudo apt-get install lua-socket sudo apt-get install luarocks sudo luarocks install luasec sudo luarocks install luasocket sudo luarocks install basexx
Then check if they have all installed ok
luarocks list
Which should show
Installed rocks: ---------------- basexx 0.3.0-1 (installed) - /usr/local/lib/luarocks/rocks luasec 0.6-1 (installed) - /usr/local/lib/luarocks/rocks luasocket 3.0rc1-2 (installed) - /usr/local/lib/luarocks/rocks
If you get a problem such as openssl.a or openssl.so or openssl.so.* not found
This may be an issue withe where different systems install/store files
try
sudo apt-get install libssl-dev
sudo mkdir /usr/local/ssl sudo cp /usr/lib/arm-linux-gnueabihf/libssl.* /usr/local/ssl sudo luarocks install luasec OPENSSL_LIBDIR=/usr/local/ssl
then check
luarocks list
N.B. If you had lua5.1 installed you may need to copy some files across to lua5.2 as they appear to install differently.
sudo cp /usr/local/share/lua/5.1/*.lua /usr/local/share/lua/5.2
Hopefully, your system now has everything in place now to run a simple script to detect your EQ3 MAX! devices.
Installing EQ3 MAX! Devices
Once you have powered up your cube and connected it to your network via the ethernet lead, you will be able to find its local IP address and network name.
Next visit https://max.eq-3.de/login.jsp and download the Windows/Mac OS software. Unfortunately no Linux option is available. This software will enable you to access your cube, create an EQ3 account and add devices.
Each device needs to be "taught in" as per the manual. During this process you can name each device and create names for each device.
Device names are critical to how Domoticz will "see" your devices. Later, you will create dummy devices that must have exactly the same name (including capitals and spacing etc.) as used in the EQ3 App.
If you are not a coder or just want an easy life, the following naming structure will work with the scripts without much head scratching.
- Sitting Room
- Sitting Room-Stat
- Sitting Room-Rad
- Sitting Room-Sens
- Kitchen
- Kitchen-Stat
- Kitchen-Rad
- Kitchen-Sens
- Another Room
- Another Room-Stat
- Another Room-Rad
- Another Room-Sens
The reasoning for this is that the second script for heating control looks for "-Stat" to identify a Wall Thermostat and "-Rad" to identify a Radiator Thermostat. The first script uses the "-Sens" suffix to identify contact sensors. If you have more than one radiator or contact sensor per room, you can name them e.g. "Kitchen left-Rad" and "Kitchen right-Rad" or "Sitting Room window-Sens" and "Sitting Room door-Sens".
N.B. Make sure all rooms are set to "Manual" before exiting the App.
Now if we have at least one room set up we can move on to testing.
Initial Testing
Create a lua file called maxtest.lua and copy and paste this script into it. Then edit the IP details to match your Cube's details.
--maxtest.lua
package.loadlib("core.so", "*")
local Socket = require "socket"
local Basexx = require "basexx"
-- Enter your cube name or ip address and port here
--MaxIP='192.168.1.5'
MaxIP='LEQ1281041'
MaxPort=62910
local Rooms = {}
local Devices = {}
function maxCmd_H(data)
-- print('H='..data)
end
function maxCmd_M(data)
i = 0
j = 0
while true do -- find 'next' comma
i = string.find(data, ",", i+1)
if not i then break end
j = i
end
s = data:sub(j+1)
dec = Basexx.from_base64(s)
num_rooms = string.byte(dec,3)
pos=4
for i=1, num_rooms do
room_num = string.byte(dec, pos)
name_len = string.byte(dec, pos+1)
pos = pos+2
name = dec:sub(pos, pos+name_len)
pos = pos+name_len
adr = Basexx.to_hex(dec:sub(pos, pos+2))
Rooms[adr] = name
pos = pos+3
end
print("Rooms\n-----")
for adr, name in pairs(Rooms) do
print(name, adr)
end
num_devs = string.byte(dec, pos)
for i=1, num_devs do
dtype = string.byte(dec, pos+1)
adr = Basexx.to_hex(dec:sub(pos+2, pos+4))
snum = dec:sub(pos+5, pos+14)
name_len = string.byte(dec, pos+15)
pos = pos+16
name = dec:sub(pos, pos+name_len)
pos = pos+name_len
room_num = string.byte(dec, pos)
Devices[adr] = name
end
print("\nDevices\n-------")
for adr, name in pairs(Devices) do
print(name, adr)
end
end
function maxCmd_C(data)
-- print('C='..data)
end
function maxCmd_L(data)
print("\nDevice status\n-------------")
pos = 1
dec = Basexx.from_base64(data)
L_hex = Basexx.to_hex(dec)
L_len = string.len(L_hex)
while (pos < L_len) do
s = L_hex:sub(pos,(pos+1))
data_len = tonumber(s,16) + 1
hex = L_hex:sub(pos,pos+(data_len*2))
adr = hex:sub(3,8)
name = Devices[adr]
if not name then name=adr end
valve_info = tonumber(hex:sub(13,14),16)
batt = bit32.extract(valve_info,7,1)
bst = bit32.extract(valve_info,3,1)
mode = bit32.extract(valve_info,0,2)
if (batt==0) then sbat="OK" else sbat="Low" end
if (mode==0) then smode="Auto" elseif (mode==1) then smode="Manual"
elseif (mode==2) then smode="Holiday" elseif (mode==3) then smode="Boost" end
if (data_len == 13) then -- WallMountedThermostat (dev_type 3)
valve_pos = -1
s = hex:sub(17,18)
setpoint = tonumber(s,16) / 2
s = hex:sub(25,26)
temp = tonumber(s,16) / 10
dtype = "Thermostat"
elseif (data_len == 12) then -- HeatingThermostat (dev_type 1 or 2)
s = hex:sub(15,16)
valve_pos = tonumber(s,16)
s = hex:sub(17,18)
setpoint = tonumber(s,16) / 2
s = hex:sub(21,22)
temp = tonumber(s,16) / 10
dtype = "Valve "
end
print(dtype, name, "Setpoint="..setpoint, "Temp="..temp, "Valve pos="..valve_pos, "Battery="..sbat, "Mode="..smode)
pos = pos + (data_len*2)
end
end
local tcp = Socket.connect(MaxIP, MaxPort)
if not tcp then
print("Socket connect failed for "..MaxIP..':'..MaxPort)
return
end
tcp:settimeout(2)
while true do
s, status, partial = tcp:receive()
if (status) then
print("TCP receive - "..status)
break
end
local line = (s or partial)
local cmd = line:sub(1,1)
local data = line:sub(3)
if (cmd == 'H') then
maxCmd_H(data)
elseif (cmd == 'M') then
maxCmd_M(data)
elseif (cmd == 'C') then
maxCmd_C(data)
elseif (cmd == 'L') then
maxCmd_L(data)
break
end
end
tcp:close()
Run it with
lua maxtest.lua
Make sure to close the Max app, the cube only accepts 1 connection.
If everything is ok, this test script will gather information from each of your devices that looks something like this...
Thermostat SittingRoom-Stat Setpoint=20.5 Temp=20.4 Valve pos=-1 Battery=OK Mode=Manual Valve SittingRoom-Rad Setpoint=20.5 Temp=0 Valve pos=80 Battery=OK Mode=Manual
If not, go back over your previous steps and look for an error, failing that ask in the forum https://www.domoticz.com/forum/viewtopic.php?f=34&t=841 and one of us can hopefully help you.
Dummy Devices
It is a good idea to create a new "Dummy" in the hardware tab and give it a name such as "EQ3", "MAX!" or "Heating" as it keeps things organised and keeps the OCD at bay.
Now for each room create...
- A "Dummy Temperature Sensor" with exactly the same name as your room (as defined in MAX!), i.e. "Sitting Room" (Only necessary when you have wall thermostats)
- A "Dummy Percentage" with exactly the same name as your radiator valve (as defined in MAX!), i.e. "Sitting Room-Rad"
- A "Dummy Thermostat Setpoint" with exactly the same name as your thermostat (as defined in MAX!), i.e. "Sitting Room-Stat" (If you don't have wall thermostats, name the Domoticz thermostats after your radiator valves but then with the "-Stat" suffix instead of "-Rad")
You see that there is no dummy device for the Window/Door contact sensors. this is because the're not(yet) integrated into domoticz.
Scripts
There are two scripts that I use. The first (which is necessary) to "Read" the cube and change setpoints and a second (optional/personal choice) to use the information gathered by the first to actually control my heating.
Max Script
This is the script that extracts all the juicy information from the cube and drops it into those dummy devices we made earlier.
Create a file called script_time_max.lua and paste this script into it. Remember to edit the "MaxIP" and "MaxPort" to match your Cube, and change the value of "DomoticzPort" if you don't use the default 8080. If you don't have wall mounted thermostats in every room, change the value of "useWMT" into false. This will use your valves instead of your thermostats to synchronize the thermostat setpoints.
--./script_time_max.lua
----------------------------------------------------------------------------------------------------------
-- Script parameters
----------------------------------------------------------------------------------------------------------
package.loadlib("core.so", "*")
local Socket = require "socket"
local Basexx = require "basexx"
local MaxIP='192.168.178.46'
local MaxPort = 62910
local useWMT = true --Set to true if there is a wall mounted thermostat in every room
local interval = 5 --Polling interval in minutes, possible range 1-59. Cube doesn't seem to like too frequent communication, 5 minutes is a safe value
local DomoticzPort = 8080
local Rooms = {}
local Devices = {}
local Room_nums = {}
function age(timestring)
t = {}
t.year = string.sub(timestring,1,4)
t.month = string.sub(timestring,6,7)
t.day = string.sub(timestring,9,10)
t.hour = string.sub(timestring,12,13)
t.min = string.sub(timestring,15,16)
t.sec = string.sub(timestring,18,19)
return os.difftime(os.time(),os.time(t))
end
function maxCmd_H(data)
-- print('H='..data)
end
function maxCmd_M(data)
i = 0
j = 0
while true do -- find next comma
i = string.find(data, ",", i+1)
if not i then break end
j = i
end
s = data:sub(j+1)
dec = Basexx.from_base64(s)
num_rooms = string.byte(dec,3)
pos=4
for i=1, num_rooms do
room_num = string.byte(dec, pos)
name_len = string.byte(dec, pos+1)
pos = pos+2
name = dec:sub(pos, pos+name_len-1)
pos = pos+name_len
adr = Basexx.to_hex(dec:sub(pos, pos+2))
Rooms[room_num] = name
pos = pos+3
end
num_devs = string.byte(dec, pos)
for i=1, num_devs do
dtype = string.byte(dec, pos+1)
adr = Basexx.to_hex(dec:sub(pos+2, pos+4))
snum = dec:sub(pos+5, pos+14)
name_len = string.byte(dec, pos+15)
pos = pos+16
name = dec:sub(pos, pos+name_len-1)
pos = pos+name_len
room_num = string.byte(dec, pos)
Room_nums[adr] = room_num
Devices[adr] = name
end
end
function maxCmd_C(data)
-- print('C='..data)
end
function maxCmd_L(data)
pos = 1
dec = Basexx.from_base64(data)
L_hex = Basexx.to_hex(dec)
L_len = string.len(L_hex)
while (pos < L_len) do
s = L_hex:sub(pos,(pos+1))
data_len = tonumber(s,16) + 1
hex = L_hex:sub(pos,pos+(data_len*2))
adr = hex:sub(3,8)
room_num = string.format("%02X", Room_nums[adr])
room = Rooms[Room_nums[adr]]
name = Devices[adr]
if not name then name=adr end
valve_info = tonumber(hex:sub(13,14),16)
batt = bit32.extract(valve_info,7,1)
bst = bit32.extract(valve_info,3,1)
mode = bit32.extract(valve_info,0,2)
if (batt==0) then sbat="OK" else sbat="Low" end
if (mode==0) then smode="Auto" elseif (mode==1) then smode="Manual"
elseif (mode==2) then smode="Holiday" elseif (mode==3) then smode="Boost" end
if (data_len == 13) then -- WallMountedThermostat (dev_type 3)
valve_pos = -1
s = hex:sub(17,18)
setpoint = tonumber(s,16) / 2
s = hex:sub(23,26)
temp = tonumber(s,16) / 10
dtype = "Thermostat"
elseif (data_len == 12) then -- HeatingThermostat (dev_type 1 or 2)
s = hex:sub(15,16)
valve_pos = tonumber(s,16)
s = hex:sub(17,18)
setpoint = tonumber(s,16) / 2
if (mode ~= 2) then
s = hex:sub(19,22)
temp = tonumber(s,16) / 10
else
temp = 0
end
dtype = "Valve"
end
--Following two lines correct temperatures over 25.5 degrees, since e.g. 26 degrees is reported as 0.5 degrees
--This is due to the fact that temperatures seem to be stored as two Hex characters only (= max 255 in decimal)
--Pending better solution
if temp < 5 then temp = temp + 25.5 end
if setpoint > 50 then setpoint = setpoint - 64 end
-- Update virtual devices in Domoticz and update MAX! setpoints if necessary
--print(dtype.." "..name.." Setpoint="..setpoint.." Temp="..temp.." Valve pos="..valve_pos)
if dtype == "Valve" and name:sub(-5,-1) ~= "-Sens" then
table.insert(commandArray, { ['UpdateDevice'] = otherdevices_idx[name]..'|0|'..valve_pos})
if not useWMT then --Use valve to update temperature and synchronize setpoints
name = name:sub(1,-5) .. "-Stat" -- set thermostat name to valve name with "-Stat" instead of "-Rad" suffix
setpoint_Domoticz = tonumber(otherdevices_svalues[name])
if setpoint_Domoticz ~= setpoint then
if age(otherdevices_lastupdate[name]) > interval * 60 then --Domoticz thermostat value must be updated
table.insert(commandArray, { ['OpenURL'] = 'http://127.0.0.1:'..DomoticzPort..'/json.htm?type=command¶m=udevice&idx='..otherdevices_idx[name]..'&nvalue=0&svalue='..setpoint})
print('Domoticz setpoint ' .. name .. ' updated')
else --Max! setpoint must be updated
MaxCmdSend(adr, room_num, "manual", setpoint_Domoticz)
print('MAX! setpoint ' .. name .. ' updated')
end
end
end
elseif dtype == "Thermostat" then
table.insert(commandArray, { ['UpdateDevice'] = otherdevices_idx[room]..'|0|'..temp})
setpoint_Domoticz = tonumber(otherdevices_svalues[name])
if setpoint_Domoticz ~= setpoint then
if age(otherdevices_lastupdate[name]) > interval * 60 then --Domoticz thermostat value must be updated
table.insert(commandArray, { ['OpenURL'] = 'http://127.0.0.1:'..DomoticzPort..'/json.htm?type=command¶m=udevice&idx='..otherdevices_idx[name]..'&nvalue=0&svalue='..setpoint})
print('Domoticz setpoint ' .. name .. ' updated')
else --Max! setpoint must be updated
MaxCmdSend(adr, room_num, "manual", setpoint_Domoticz)
print('MAX! setpoint ' .. name .. ' updated')
end
end
end
pos = pos + (data_len*2)
end
end
function MaxCmdSend(id, room, mode, setpoint)
bits = setpoint * 2
smode = string.upper(mode)
if smode == 'MANUAL' then
bits = 64 + bits
elseif smode == 'BOOST' then
bits = 192 + bits
elseif smode == 'VACATION' then
bits = 128 + bits
end
hex = "000440000000"..id..room..string.format("%x",bits)
sendStr = Basexx.to_base64(Basexx.from_hex(hex))
i, status = tcp:send("s:"..sendStr.."\r\n")
if not i then
print("MAX TCP send failed - "..status)
return
end
end
commandArray = {}
local m = os.date('%M')
if (m % interval == 0) and (m ~= 0) then
tcp = Socket.connect(MaxIP, MaxPort)
if not tcp then
print("Socket connect failed for "..MaxIP..':'..MaxPort)
return
end
tcp:settimeout(2)
while true do
s, status, partial = tcp:receive()
if (status) then
print("TCP receive - "..status)
break
end
local line = (s or partial)
local cmd = line:sub(1,1)
local data = line:sub(3)
if (cmd == 'H') then
status = maxCmd_H(data)
if status == 'Error' then break end
elseif (cmd == 'M') then
maxCmd_M(data)
elseif (cmd == 'C') then
maxCmd_C(data)
elseif (cmd == 'L') then
maxCmd_L(data)
break
end
end
tcp:close()
end
return commandArray
Place the script in your domoticz/scripts/lua folder (pasting into the internal editor should work just as well) and soon the Dummy Devices should start to show data.
If you will see error in Domoticz log you should do this:
mv usrlocalsharelua5p2.tar.gz /usr/local/share/lua/5.2/ mv usrlocalliblua5p2.tar.gz /usr/local/lib/lua/5.2/ cd /usr/local/share/lua/5.2/ sudo tar -xvf usrlocalsharelua5p2.tar.gz sudo rm usrlocalsharelua5p2.tar.gz cd /usr/local/lib/lua/5.2/ sudo tar -xvf usrlocalliblua5p2.tar.gz rm usrlocalliblua5p2.tar.gz
Files: usrlocalsharelua5p2.tar.gz usrlocalliblua5p2.tar.gz
Valves Script
This script uses the information from the new devices to control a boiler and is an example of how I use the system.
I have
- A dummy switch called "Heating"
- A dummy switch called "Holiday"
- A Fibaro Double Relay called "CH_Switch" and "HW_Switch"
- A "Selector Switch" with Off|Hot Water|Heating|Holiday
The "Selector Switch" triggers one of four scenes
- Off = Heating off + HW_Switch off + Holiday Off
- Hot Water = Heating off + HW_Switch on + Holiday Off
- Heating = Heating on + HW_Switch off + Holiday Off
- Holiday = Heating off + HW_Switch off + Holiday On
From within the switch settings I can set timers for when I want heating or hot water.
Don't forget that the "Thermostat Setpoint" also has in-built timers so you can raise and lower temperatures for different times of the day.
This script looks not only at temperatures but how much the valves are open and calling for heat. It has saved me fuel over the last winter by this method.
Ok, so here's script_time-Valves.lua to make use of it you will need to edit the devices to suit your needs.
There are some "Preset Values" at the beginning of the script that will need to reflect your needs.
You will also need to change the "Switch" variables to match your switches.
-- script_time_Heating Valves.lua
-- Version 2.0 07/11/16
-- Script to read the % open of radiator valves
-- All radiator valves are labelled "<room name>-Rad"
-- search is made for "-Rad" to indicate a radiator valve
-- If found it will be interrogated for % open value
-- Thermostat are named <room name>-Stat so a search is made for "-Stat" to indicate thermostats
-- If found it will be interrogated for temperature value
-- If demand is greater than BoilerOnPercent value then fire up boiler
-- If demand is less than BoilerOnPercent minus HysterysisOffPercent then switch off boiler
-- Preset Values
BoilerOnPercent = 30 -- percentage valve open at which the boiler will be turned on
HysterysisOffPercent = 20 -- percentage below BoilerOnPercent to switch off the boiler
MinValves = 2 -- Number of Valves that need to be open before boiler is turned on
ValvePercentOveride = 75 -- Percentage value of valve open required to override MinValves value (one room is very cold)
HolidayMinTemp = 10 -- Minimum room temperature before boiler is turned on during holiday period
HolidayHysterysisTemp = 2 -- Value to increase house temperature by while in holiday mode if boiler is turned on due to low temperatures
MissingDevicesTime = 86400 -- Value in seconds to allow before reporting a device has not been updated
email = "[email protected]" -- email address for warnings
-- Script Variables
PercentMax = 0
TempMin = 10
ValveCount = 0
MissingDeviceCount = 0
SendAnEmail = false
HeatingSwitch = "Heating"
BoilerSwitch = "CH_Switch"
HolidaySwitch = "Holiday"
-- Set printing to log options (true / false)
printData = true
printDebug = false
-- Get current date & time
t1 = os.time()
local currentDate = os.date("*t"); -- sets up currentDate.[table]
-- (currentDate.year [full], .month [1-12], .day [1-31], .hour [0-23], .min [0-59], .sec [0-59], .wday [0-6 {Sun-Sat}])
sCurrentTime = currentDate.year .. "-" .. currentDate.month .. "-" .. currentDate.day .. " " .. currentDate.hour .. ":" .. currentDate.min .. ":" .. currentDate.sec
function TimeElapsed(s) -- expects date & time in the form of 2010-01-23 12:34:56
year = string.sub(s, 1, 4)
month = string.sub(s, 6, 7)
day = string.sub(s, 9, 10)
hour = string.sub(s, 12, 13)
minutes = string.sub(s, 15, 16)
seconds = string.sub(s, 18, 19)
t1 = os.time()
t2 = os.time{year=year, month=month, day=day, hour=hour, min=minutes, sec=seconds}
difference = os.difftime (t1, t2)
return difference -- in seconds
end
commandArray = {}
-- print blank line in log
if printData == true then
print (" ")
print (" *** Heating Script Output ***")
print (" ")
end
-- Get Data from Radiator Valves
for i, v in pairs(otherdevices) do -- Get all devices in the database
v = i:sub(-4,-1) -- Grab the last four characters of the device name
if (v == '-Rad') then -- are the last four characters "-Rad"? If so we have a Radiator Valve
RoomName = i:sub(1,-5) -- Get the rest of the name, which will be the room name
sSetTempValue = otherdevices_svalues[RoomName..'-Stat']
sValvePercentOpen = otherdevices_svalues[i]
sLastUpdateTime = otherdevices_lastupdate[i]
sElapsedTime = TimeElapsed(otherdevices_lastupdate[i])
message = RoomName .. " valve is open " .. sValvePercentOpen .. " percent " .. " Setpoint temperature is " .. sSetTempValue .. "C" -- for debug
message2 = RoomName .. " last seen " .. sLastUpdateTime .. " Elapsed time " .. sElapsedTime
if printData == true then
print (message)
print (message2)
end
-- check for missing devices
if sElapsedTime > MissingDevicesTime then
SendAnEmail = false
MissingDeviceCount = MissingDeviceCount + 1
end
-- get the % value of the most open Radiator Valve
if tonumber(sValvePercentOpen) > PercentMax then
PercentMax = tonumber(sValvePercentOpen)
end
-- Count the number of valves that are open more than BoilerOnPercent
if tonumber(sValvePercentOpen) >= BoilerOnPercent then
ValveCount = ValveCount + 1
end
end
end
if printData == true then
print (" ")
end
-- Get Data from Thermostats
for i, v in pairs(otherdevices) do -- Get all devices in the database
v = i:sub(-5,-1) -- Grab the last five characters of the device name
if (v == '-Stat') then -- are the last five characters "-Stat "? If so we have an EQ-3 Thermostat
RoomName = i:sub(1,-6) -- Get the rest of the name, which will be the room name
sTemp = otherdevices_svalues[i] -- get the temperature
sLastUpdateTime = otherdevices_lastupdate[i]
sElapsedTime = TimeElapsed(otherdevices_lastupdate[i])
message = RoomName.." temperature is " .. sTemp .. " Centigrade " -- for debug
message2 = RoomName .. " last seen " .. sLastUpdateTime .. " Elapsed time " .. sElapsedTime
if printData == true then
print(message)
print(message2)
end
-- get the lowest temperature of the thermostats
if tonumber(sTemp) < TempMin then
TempMin = tonumber(sTemp)
end
-- check for missing devices
if sElapsedTime > MissingDevicesTime then
SendAnEmail = true -- change this to false if you do not require emails to be sent
MissingDeviceCount = MissingDeviceCount + 1
end
end
end
if printData == true then
print (" ")
print ("Number of valves open more than " .. BoilerOnPercent .. "% is " .. ValveCount .." valves")
print("Highest valve open value is " .. PercentMax .." percent ")
print("Lowest thermostat reading is " .. TempMin .." Centigrade ")
print (" ")
end
if printData == true then
if (otherdevices[BoilerSwitch] == 'On')then
print ("Current state - Boiler is ON ")
else
print ("Current state - Boiler is OFF ")
end
end
-- Check the elapsed time and email if overdue
if (SendAnEmail == true) then
print (" ")
print("Heating Script: missing device email sent to " .. (email) );
notifyString = os.date("Domoticz Alert # The current time is %X on %A Lost contact with " .. MissingDeviceCount .. " Heating script devices, check if Max!Buddy is running. #") .. (email)
commandArray['SendEmail'] = notifyString
end
-- Perform logic
if printDebug == true then
-- view the settings to understand logic performance
print ("PercentMax (" .. PercentMax .. "%) " .. "Boiler On value (" .. BoilerOnPercent .. "%) " .. "Boiler Off value (" .. (BoilerOnPercent - HysterysisOffPercent) .. ")% ")
print ("Number of valves open more than " .. BoilerOnPercent .. "% is " .. ValveCount .." valves. Minimum valves setting " .. MinValves )
print ("Maximum open value " .. PercentMax .. "%" .. " Override value " .. ValvePercentOveride .."%")
print (" ")
end
if (otherdevices[HolidaySwitch] == 'Off')then -- Not on holiday
if (otherdevices[HeatingSwitch] == 'On')then -- It's time to heat the house
if (otherdevices[BoilerSwitch] == 'Off') then --If a minimum of 'MinValves' valves are on by more that pre-set value BoilerOnPercent
if printDebug == true then
print ("Test passed - Boiler is OFF ")
end
if (PercentMax > BoilerOnPercent) then
if printDebug == true then
print ("Test passed - Radiators are open beyond the threshold ")
end
if (ValveCount >= MinValves) or (BoilerOnPercent >= ValvePercentOveride) then
if printDebug == true then
print ("Test passed - Either multiple valves are open or override count is reached ")
end
commandArray[BoilerSwitch]='On' -- turn on boiler
if printData == true then
print ("Command sent - Turn ON Boiler ")
end
end
end
end
end
if (PercentMax < (BoilerOnPercent - HysterysisOffPercent) or (ValveCount < MinValves)) and (otherdevices[BoilerSwitch] == 'On') then -- If the number of valves open more than BoilerOnPercent minus HysterysisOffPercent
commandArray[BoilerSwitch]='Off' -- turn off boiler
if printData == true then
print ("Command sent - Turn OFF Boiler ")
end
end
else -- on holiday
if (TempMin <= HolidayMinTemp) and (otherdevices[BoilerSwitch] == 'Off') then -- house is very cold
commandArray[BoilerSwitch]='On' -- turn on boiler
end
if (TempMin >= (HolidayMinTemp + HolidayHysterysisTemp)) and (otherdevices[BoilerSwitch] == 'On') then -- house is warm enough
commandArray[BoilerSwitch]='Off' -- turn on boiler
end
end
if printData == true then
print (" ")
end
return commandArray
CREDITS
None of this would exist without the hard work of a group of people pulling together with a common cause and they are, in no particular order and not exclusively
- The Domoticz Devs
- Westcott
- l0gic
- mvzut
- All the guys that tested
- Everyone who asked an intelligent question
- Anyone I forgot!