Hello all,
This is my first post. I've been reading (silently) for a while, and developing myself.
Recently I've introduced Toon to my Domoticz network. The cool thing here is that this Toon is hacked. I would like to share my experience and scripts.
First a bit of background info:
A while ago I was introduced to Domoticz. After buying the Aeon Z-Wave USB stick and a couple of Z-wave devices, I wanted to connect my thermostat.
Because I was using the Honeywell Chronoterm this was impossible.
Soon after I decided to buy a second-hand Toon. After receiving the package and installing the device I was quickly confronted with my own stupidity: I didn't research enough about the Toon and discovered that a second-hand Toon is basically useless. I could not install the device as I did not have the registration key. This resulted in the inability of completing the installation wizard, and only being able to control the temperature with the +/- buttons. Not only that, but I was unable to control the Toon from Domoticz, or any other software for that matter.
This was obviously a (major) set back from the Chronoterm I had been using.
All of these features on the Toon will be unlocked once you purchase a subscription from Eneco (€5 a month if you're not a client). This seemed to be an obvious solution but unfortunately, Eneco doesn't sell these subscriptions to people with a second-hand Toon.
The situation seemed dire.
After (a lot) of research, I came to discover that I was not the only one in this predicament.
http://www.domoticaforum.eu/viewtopic.p ... ica#p79224
This seemed to be the ultimate solution: Rooting/hacking the Toon. Completely going around Eneco.
Of course I wanted to do this but I did not have the proper knowledge to execute (the quite carefully explained) instructions on the above link.
After contacting the inventor of this hack, he quickly replied telling me he was glad to help.
So big big big thanks and all credit goes to marcelr.
So, now I had a hacked Toon. I was able to use the Toon as it was intended, including the programs, etc. etc. But since I doubt there are more than 10 of these hacked Toons in the Netherlands, I didn't really have the proper documentation on how to use the device in combination with Domoticz.
I did research, tested a lot, tested some more, and finally came up with the solutions as it is currently implemented in Domoticz.
Sorry for the long story, I will go on to explain the solution in Domoticz, sharing it for all to use.
Step 1: Controlling the Toon from Domoticz
Requirements;
- Hacked Toon
- Open Toon firewall
- Preconfigured port (in my case port 80)
- All variables that end with "SensorName" are intended to make the scripts a little bit more flexible. Just enter the names of your devices in these variables
- ToonIP - doesn't need explanation
- ToonChangedByDomoticz - Unfortunately one of my Toon scripts triggers another, I use this UV to stop this from happening.
Toon Temperature, Toon Thermostat and Toon Program Information are dummy devices.
Obviously the 2 selector switches operate themselves.
Now the script to operate the setpoint "Toon Thermostat", it's a lua script triggered by device:
Code: Select all
-- Script used for Toon Thermostaat utility device, upon changing temp in Domoticz, temperature is sent to Toon.
--
commandArray = {}
ToonThermostatSensorName = uservariables['UV_ToonThermostatSensorName'] -- Sensor showing current setpoint
ToonIP = uservariables['UV_ToonIP']
for deviceName,deviceValue in pairs(devicechanged) do
if (deviceName == ToonThermostatSensorName) then
if uservariables['UV_ToonChangedByDomoticz'] == 1 then
commandArray['Variable:UV_ToonChangedByDomoticz'] = '0'
else
SetPoint = otherdevices_svalues[ToonThermostatSensorName]
ToonCommand = string.format('http://%s/happ_thermstat?action=setSetpoint&Setpoint=%s', ToonIP, SetPoint*100)
print('Setting Toon setpoint to '.. SetPoint)
commandArray['OpenURL'] = ToonCommand
end
end
end
return commandArray
The script. It's a time script that runs every minute:
Code: Select all
-- Time script runs every minute, intended to sync Domoticz with Toon in case the value is changed on the physical device.
-- Updates Toon Thermostat Sensor to value set on Toon
-- Updates Toon Temperature Sensor to value set on Toon
-- Updates Toon Scenes switch based on program set on Toon
-- Updates Toon Auto Program switch to value set on Toon
-- Updates Toon program information text to value set on Toon
commandArray = {}
ToonThermostatSensorName = uservariables['UV_ToonThermostatSensorName'] -- Sensor showing current setpoint
ToonTemperatureSensorName = uservariables['UV_ToonTemperatureSensorName'] -- Sensor showing current room temperature
ToonScenesSensorName = uservariables['UV_ToonScenesSensorName'] -- Sensor showing current program
ToonAutoProgramSensorName = uservariables['UV_ToonAutoProgramSensorName'] -- Sensor showing current auto program status
ToonProgramInformationSensorName = uservariables['UV_ToonProgramInformationSensorName'] -- Sensor showing displaying program information status
ToonIP = uservariables['UV_ToonIP']
DomoticzIP = uservariables['UV_DomoticzIP']
json = assert(loadfile "C:\\Program Files (x86)\\Domoticz\\scripts\\lua\\json.lua")() -- For Windows
local handle = assert(io.popen(string.format('curl http://%s/happ_thermstat?action=getThermostatInfo', ToonIP)))
local ThermostatInfo = handle:read('*all')
handle:close()
jsonThermostatInfo = json:decode(ThermostatInfo)
currentSetpoint = tonumber(jsonThermostatInfo.currentSetpoint) / 100
currentTemperature = tonumber(jsonThermostatInfo.currentTemp) / 100
currentProgramState = tonumber(jsonThermostatInfo.programState)
currentActiveState = tonumber(jsonThermostatInfo.activeState)
currentNextTime = jsonThermostatInfo.nextTime
currentNextSetPoint = tonumber(jsonThermostatInfo.nextSetpoint) / 100
-- Update the thermostat sensor to current setpoint
if otherdevices_svalues[ToonThermostatSensorName]*100 ~= currentSetpoint*100 then
print('Updating thermostat sensor to new set point: ' ..currentSetpoint)
commandArray[1] = {['Variable:UV_ToonChangedByDomoticz'] = '1'} -- Set variable changed to 1 to prevent script ToonSetPoint from shooting an event at Toon
commandArray[2] = {['UpdateDevice'] = string.format('%s|0|%s', otherdevices_idx[ToonThermostatSensorName], currentSetpoint)}
end
-- Update the temperature sensor to current room temperature
if otherdevices_svalues[ToonTemperatureSensorName]*100 ~= currentTemperature*100 then
print('Updating the temperature sensor to new value: ' ..currentTemperature)
commandArray[3] = {['UpdateDevice'] = string.format('%s|0|%s', otherdevices_idx[ToonTemperatureSensorName], currentTemperature)}
end
-- Update the toon scene selector sensor to current program state
CurrentToonScenesSensorValue = otherdevices_svalues[ToonScenesSensorName]
if currentActiveState == -1 then currentActiveState = '50' -- Manual
elseif currentActiveState == 0 then currentActiveState = '40' -- Comfort
elseif currentActiveState == 1 then currentActiveState = '30' -- Home
elseif currentActiveState == 2 then currentActiveState = '20' -- Sleep
elseif currentActiveState == 3 then currentActiveState = '10' -- Away
end
if CurrentToonScenesSensorValue ~= currentActiveState then -- Update toon selector if it has changed
print ('Updating Toon Scenes selector')
commandArray[4] = {['UpdateDevice'] = string.format('%s|1|%s', otherdevices_idx[ToonScenesSensorName], currentActiveState)}
end
-- Updates the toon auto program switch
CurrentToonAutoProgramSensorValue = otherdevices_svalues[ToonAutoProgramSensorName]
if currentProgramState == 0 then currentProgramState = '10' -- No
elseif currentProgramState == 1 then currentProgramState = '20' -- Yes
elseif currentProgramState == 2 then currentProgramState = '30' -- Temporary
end
if CurrentToonAutoProgramSensorValue ~= currentProgramState then -- Update toon auto program selector if it has changed
print ('Updating Toon Auto Program selector')
commandArray[5] = {['UpdateDevice'] = string.format('%s|1|%s', otherdevices_idx[ToonAutoProgramSensorName], currentProgramState)}
end
-- Updates the toon program information text box
CurrentToomProgramInformationSensorValue = otherdevices_svalues[ToonProgramInformationSensorName]
if currentNextTime == 0 or currentNextSetPoint == 0 then
ToonProgramInformationSensorValue = 'Op ' ..currentSetpoint.. '°'
else
ToonProgramInformationSensorValue = 'Om ' ..os.date('%H:%M', currentNextTime).. ' op ' ..currentNextSetPoint.. '°'
end
if CurrentToomProgramInformationSensorValue ~= ToonProgramInformationSensorValue then
commandArray[6] = {['UpdateDevice'] = string.format('%s|0|%s', otherdevices_idx[ToonProgramInformationSensorName], ToonProgramInformationSensorValue)}
end
--
return commandArray
I have tried to program the script and configure the selector switches in such a way that it is intuitive to operate, and identical to operation on the Toon. If you are familiar with the operation of the Toon then these scripts and devices should make sense to you. If you have any further questions please don't hesitate to ask.
Problems:
Unfortunately my Time script triggers the device script when updating the thermostat with the new setpoint. Because I used a user variable to stop this from happening, it is essentially not a problem. However I get warnings in my log that the script has been running for more than 10 seconds. This ONLY happens when the thermostat is updating the setpoint. If anybody knows a more efficient way of stopping the device script from being triggered, and thus removing the warning from my log, I would be very happy.
EDIT:
I made the 2 scripts in DzVents. The main advantages are that there is less code and that the new function .silent() in DzVents 2.3.0 should prevent the scripts from looping, this fixes a bug I've always had in the above scripts.
Right now the .silent() doesn't seem to be working properly, I've reported this to dannybloe & I'll update this script accordingly if necessary.
In one way the script is a little bit less flexible because I don't think it's possible to refer to a uservariable in the devices{} tag. I'm thereby forced to put the name/IDX of the device in the script. Previously I put this name in a uservariable making the script extremely flexible.
ToonSetPoint
Code: Select all
return {
on = {
devices = {
'Toon Thermostat'
}
},
execute = function(domoticz, device)
domoticz.openURL(string.format('http://%s/happ_thermstat?action=setSetpoint&Setpoint=%s', domoticz.variables('UV_ToonIP').value, device.SetPoint*100))
domoticz.log('Setting Toon setpoint to '.. device.SetPoint)
end
}
Code: Select all
return {
on = {
timer = {
'every minute'
}
},
execute = function(domoticz)
local ToonThermostatSensorName = domoticz.variables('UV_ToonThermostatSensorName').value -- Sensor showing current setpoint
local ToonTemperatureSensorName = domoticz.variables('UV_ToonTemperatureSensorName').value -- Sensor showing current room temperature
local ToonScenesSensorName = domoticz.variables('UV_ToonScenesSensorName').value -- Sensor showing current program
local ToonAutoProgramSensorName = domoticz.variables('UV_ToonAutoProgramSensorName').value -- Sensor showing current auto program status
local ToonProgramInformationSensorName = domoticz.variables('UV_ToonProgramInformationSensorName').value -- Sensor showing displaying program information status
local ToonIP = domoticz.variables('UV_ToonIP').value
local DomoticzIP = domoticz.variables('UV_DomoticzIP').value
-- Handle json
--local json = assert(loadfile "C:\\Program Files (x86)\\Domoticz\\scripts\\lua\\json.lua")() -- For Windows
local json = assert(loadfile "/home/maes/domoticz/scripts/lua/JSON.lua")() -- For Linux
local handle = assert(io.popen(string.format('curl http://%s/happ_thermstat?action=getThermostatInfo', ToonIP)))
local ThermostatInfo = handle:read('*all')
handle:close()
local jsonThermostatInfo = json:decode(ThermostatInfo)
if jsonThermostatInfo == nil then
return
end
local currentSetpoint = tonumber(jsonThermostatInfo.currentSetpoint) / 100
local currentTemperature = tonumber(jsonThermostatInfo.currentTemp) / 100
local currentProgramState = tonumber(jsonThermostatInfo.programState)
if currentProgramState == 0 then currentProgramState = 10 -- No
elseif currentProgramState == 1 then currentProgramState = 20 -- Yes
elseif currentProgramState == 2 then currentProgramState = 30 -- Temporary
end
local currentActiveState = tonumber(jsonThermostatInfo.activeState)
if currentActiveState == -1 then currentActiveState = 50 -- Manual
elseif currentActiveState == 0 then currentActiveState = 40 -- Comfort
elseif currentActiveState == 1 then currentActiveState = 30 -- Home
elseif currentActiveState == 2 then currentActiveState = 20 -- Sleep
elseif currentActiveState == 3 then currentActiveState = 10 -- Away
end
local currentNextTime = jsonThermostatInfo.nextTime
local currentNextSetPoint = tonumber(jsonThermostatInfo.nextSetpoint) / 100
local currentBoiletSetPoint = jsonThermostatInfo.currentInternalBoilerSetpoint
----
-- Update the thermostat sensor to current setpoint
if domoticz.devices(ToonThermostatSensorName).setPoint*100 ~= currentSetpoint*100 then
domoticz.log('Updating thermostat sensor to new set point: ' ..currentSetpoint)
domoticz.devices(ToonThermostatSensorName).updateSetPoint(currentSetpoint).silent()
end
-- Update the temperature sensor to current room temperature
if domoticz.round(domoticz.devices(ToonTemperatureSensorName).temperature, 1) ~= domoticz.round(currentTemperature, 1) then
domoticz.log('Updating the temperature sensor to new value: ' ..currentTemperature)
domoticz.devices(ToonTemperatureSensorName).updateTemperature(currentTemperature)
end
-- Update the toon scene selector sensor to current program state
if domoticz.devices(ToonScenesSensorName).level ~= currentActiveState then -- Update toon selector if it has changed
domoticz.log('Updating Toon Scenes selector to: '..currentActiveState)
domoticz.devices(ToonScenesSensorName).switchSelector(currentActiveState).silent()
end
-- Updates the toon auto program switch
if domoticz.devices(ToonAutoProgramSensorName).level ~= currentProgramState then -- Update toon auto program selector if it has changed
domoticz.log('Updating Toon Auto Program selector to: '..currentProgramState)
domoticz.devices(ToonAutoProgramSensorName).switchSelector(currentProgramState).silent()
end
-- Updates the toon program information text box
if currentNextTime == 0 or currentNextSetPoint == 0 then
ToonProgramInformationSensorValue = 'Op ' ..currentSetpoint.. '°'
else
ToonProgramInformationSensorValue = 'Om ' ..os.date('%H:%M', currentNextTime).. ' op ' ..currentNextSetPoint.. '°'
end
if domoticz.devices(ToonProgramInformationSensorName).text ~= ToonProgramInformationSensorValue then
domoticz.log('Updating Toon Program Information to: '..ToonProgramInformationSensorValue)
domoticz.devices(ToonProgramInformationSensorName).updateText(ToonProgramInformationSensorValue)
end
end
}