blackdog65 wrote:mvzut wrote:If you also want to try this, and my instructions are too sparse for you, let me know, so that I can explain in more detail.
I'm in the same situation. Everything runs like a "Swiss Watch" except for these time out errors, so I'd really like to see what you've done
Sean
Hi Sean,
Here is a version of the script that can be run outside of Domoticz using crontab (you may not recognise it at first, I polished it up quite a bit):
Code: Select all
-- Script which creates & synchronizes devices in Domoticz with values from MAX! cube
-- Change variables below to your own values
-- Start by adding new cronjob "*/5 * * * * /usr/bin/lua /home/pi/domoticz/scripts/lua/maxscript.lua" (using crontab -e)
MaxIP='192.168.178.46' -- IP adress of your Cube, best to check via your router
MaxPort = 62910 -- Port used by your Cube, usually 62910
DomoticzPort = 8080 -- Port used by Domoticz, 8080 is the default
useWMT = true -- Set to true if there is a wall mounted thermostat in every room
interval = 5 -- Polling interval in minutes, fill in same value as in crontab
setpoint_DoorOpen = 5 -- Setpoint when door/window open
json = (loadfile "/home/pi/domoticz/scripts/lua/JSON.lua")() -- uncomment if you're using Linux (comment next line)
--json = (loadfile "D:\\Domoticz\\scripts\\lua\\json.lua")() -- uncomment if you're using Windows (comment previous line)
----------------------------------------
-- No changes required below this line -
----------------------------------------
package.loadlib("core.so", "*")
Socket = require "socket"
Basexx = require "basexx"
http = require "socket.http"
Rooms = {}
Devices = {}
Types = {}
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 get_MAX_ID()
result, statuscode, content = http.request('http://127.0.0.1:'..DomoticzPort..'/json.htm?type=hardware')
r,e = json:decode(result)
if r.status == "OK" then
for k,v in pairs(r.result) do
if v.Name == "MAX!" then return v.idx end
end
end
end
function ReadThermostat(DID)
result, statuscode, content = http.request('http://127.0.0.1:'..DomoticzPort..'/json.htm?type=devices')
r,e = json:decode(result)
if r.status == "OK" then
for k,v in pairs(r.result) do
if v.ID == DID then return v.SetPoint,v.LastUpdate end
end
end
return 0, '2016-01-01 00:00:00' -- value to return if device was not found
end
function UpdateValve(DID, value)
http.request('http://127.0.0.1:'..DomoticzPort..'/json.htm?type=command¶m=udevice&hid='..MAX_ID..'&did='..DID..'&dunit=1&dtype=243&dsubtype=6&nvalue=0&svalue='..value)
end
function UpdateTemperature(DID, value)
http.request('http://127.0.0.1:'..DomoticzPort..'/json.htm?type=command¶m=udevice&hid='..MAX_ID..'&did='..DID..'&dunit=1&dtype=80&dsubtype=5&nvalue=0&svalue='..value)
end
function UpdateSwitch(DID, status)
if status == "On" then
http.request('http://127.0.0.1:'..DomoticzPort..'/json.htm?type=command¶m=udevice&hid='..MAX_ID..'&did='..DID..'&dunit=1&dtype=17&dsubtype=0&nvalue=1')
elseif status == "Off" then
http.request('http://127.0.0.1:'..DomoticzPort..'/json.htm?type=command¶m=udevice&hid='..MAX_ID..'&did='..DID..'&dunit=1&dtype=17&dsubtype=0&nvalue=0')
end
end
function SetpointSync(DID, devicename, setpoint_MAX)
setpoint_Domoticz, LastUpdate = ReadThermostat(DID)
if tonumber(setpoint_Domoticz) ~= setpoint_MAX then
if age(LastUpdate) > interval * 60 then -- Domoticz thermostat value must be updated
http.request('http://127.0.0.1:'..DomoticzPort..'/json.htm?type=command¶m=udevice&hid='..MAX_ID..'&did='..DID..'&dunit=1&dtype=242&dsubtype=1&nvalue=0&svalue='..setpoint_MAX)
print(os.date("%x %X ")..'Domoticz setpoint '..devicename..' set to '..setpoint..' degrees')
http.request('http://127.0.0.1:'..DomoticzPort..'/json.htm?type=command¶m=addlogmessage&message='..string.gsub('Domoticz setpoint '..devicename..' set to '..setpoint_MAX..' degrees',' ','%%20'))
elseif setpoint_Domoticz ~= setpoint_DoorOpen then -- MAX! setpoint must be updated
MaxCmdSend(adr, room_num, "MANUAL", setpoint_Domoticz)
print(os.date("%x %X ")..'MAX! setpoint '..devicename..' set to '..setpoint_Domoticz..' degrees')
http.request('http://127.0.0.1:'..DomoticzPort..'/json.htm?type=command¶m=addlogmessage&message='..string.gsub('MAX! setpoint '..devicename..' set to '..setpoint_Domoticz..' degrees',' ','%%20'))
end
end
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)
if num_rooms == 0 or num_rooms == nil then
http.request('http://127.0.0.1:'..DomoticzPort..'/json.htm?type=command¶m=addlogmessage&message=MAX!%20configuration%20error!')
end
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
Types[adr] = dtype
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]
dtype = Types[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 dtype == 3 then -- WallMountedThermostat
s = hex:sub(17,18)
setpoint = tonumber(s,16) / 2
if setpoint > 50 then setpoint = setpoint - 64 end
s = hex:sub(23,26)
temp = tonumber(s,16) / 10
if temp < 5 then temp = temp + 25.5 end -- temporary solution for 2 digit hex temperature limitation
UpdateTemperature(adr, temp)
SetpointSync('0'..adr, name, setpoint)
elseif dtype == 1 or dtype == 2 then -- Valve
s = hex:sub(15,16)
valve_pos = tonumber(s,16)
s = hex:sub(17,18)
setpoint = tonumber(s,16) / 2
if setpoint > 50 then setpoint = setpoint - 64 end
s = hex:sub(19,22)
temp = tonumber(s,16) / 10
if temp < 5 then temp = temp + 25.5 end -- temporary solution for 2 digit hex temperature limitation
UpdateValve(adr, valve_pos)
if not useWMT then -- Use valve for temp & setpoint
if temp ~= 0 then UpdateTemperature(adr, temp) end
SetpointSync('0'..adr, name, setpoint)
end
elseif dtype == 4 then -- Door/window sensor
if mode == 2 then UpdateSwitch(adr, "On") else UpdateSwitch(adr, "Off") 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
-----------------------
-- Main script start --
-----------------------
--Get ID of MAX hardware in Domoticz, create if it doesn't exist
MAX_ID = get_MAX_ID()
if MAX_ID == nil then -- "MAX!" dummy hardware not yet created, create it
http.request('http://127.0.0.1:'..DomoticzPort..'/json.htm?type=command¶m=addhardware&htype=15&port=1&name=MAX!&enabled=true')
end
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()
You can place it in /home/pi/domoticz/scripts/lua/ but then with a different name, e.g. maxscript.lua, so that Domoticz is not going to run it every minute. You need to remove the "old" script or rename it to script_time_max.lua.disabled or something.
After you have done this, start an SSH terminal session to your Raspberry Pi (provided your Domoticz runs on a Raspberry Pi). Start the crontab editor with
and add this line to the bottom of the file:
Code: Select all
*/5 * * * * /usr/bin/lua /home/pi/domoticz/scripts/lua/maxscript.lua
(replace script name if you used something else than my suggestion).
Press control+X, Y and enter, and off you go!
P.S. It may also work with crontab -e, i.e. without the sudo, but I use this and it works.
UPDATE 3-12-2016 11:19
I implemented door/window sensor reading in this one, switches will be automatically created for them. Just add & name them via the devices screen, and then change the switch type to "contact". Also set the vairable "setpoint_DoorOpen" to what you configured for this in MAX! This will prevent that the temporarily reduced setpoint is written back to MAX! as permanent new setpoint.