Page 1 of 2
My dzVents Bathroom Humidity Control script
Posted: Wednesday 22 March 2017 14:57
by BakSeeDaa
Below is my dzVents Bathroom Humidity Control script version 1.3.1
EDIT: Now modified to work with dzVents version 2.0.0 and higher
I've been inspired by this script:
Lua script for controlling humidity in the bathroom and I got some design tips from
@dannybloe.
My house has a single fan that extracts air from all the rooms in the house. I can force that single fan to work at a higher speed in case of that the humidity in one of my bathrooms rises quickly. My script currently supports only a single fan but multiple humidity sensors. The script also supports a button that can be pushed manually in case that the air quality in the bathroom requires that. I may improve my script and update this first post if I get any suggestions for improvements from you guys. Please feel free to suggest improvements etc.
Here it goes:
Code: Select all
--[[
bathroomHumControl.lua by BakSeeDaa
Version 1.3.1
This script controls the humidity in a typical bathroom setting by detecting
relative rises in humidity in a short period.
--]]
local FAN_DEVICE = 'Nibe F750 Force Fan' -- Fan device
local FORCE_FAN_DEVICE = 'Flatulence Button' -- (Optional)
local FORCE_FAN_TIME = 10 -- Minutes to force the fan when button pushed
local HUMIDITY_SENSORS = {'Badrum 1 C+RH', 'Badrum 2 C+RH'}
local FAN_DELTA_TRIGGER = 3 -- % Rise in humidity that will trigger the fan
local FAN_MAX_TIME = 24 -- Maximum minutes that the fan can be on in case we never reach the target humidity
local TARGET_OFFSET = 2 -- Fan goes off if target + offset is reached
local TEST_MODE_HUMIDITY_READING = 0 -- Set to a value between 1 and 100. Set to 0 to disable test mode
local READING_SAMPLES = 15
-- Create the data declarations
local data = {}
for i, device in pairs(HUMIDITY_SENSORS) do
data[device] = {history = true, maxItems = READING_SAMPLES + 1}
data['dehumidProgramActive'..i] = {history = true, maxItems = 1} -- Need history to get time stamp
data['forceFan'] = {history = true, maxItems = 1} -- Need history to get time stamp
data['targetHum'..i] = {initial=0}
end
return {
active = true,
logging = {
--level = domoticz.LOG_DEBUG, -- Select one of LOG_DEBUG, LOG_INFO, LOG_ERROR, LOG_FORCE to override system log level
marker = "bathRoom"
},
on = {
devices = {
FORCE_FAN_DEVICE
},
timer = {
'every 1 minutes'
}
},
data = data,
execute = function(domoticz, device, triggerInfo)
local forceFanReadings = domoticz.data.forceFan
if (triggerInfo.type == domoticz.EVENT_TYPE_TIMER) then
local fanCmd = 'Off'
for i = 1, #HUMIDITY_SENSORS do
local humidityReadings = domoticz.data[HUMIDITY_SENSORS[i]]
-- Store the read value in the persistant data
for j = 1, (humidityReadings.size == 0 and READING_SAMPLES + 1 or 1) do
humidityReadings.add((TEST_MODE_HUMIDITY_READING == 0
and domoticz.devices(HUMIDITY_SENSORS[i]).humidity or TEST_MODE_HUMIDITY_READING))
end
-- INIT
local programActiveReadings = domoticz.data['dehumidProgramActive'..i]
if (programActiveReadings.size == 0) then
domoticz.log('programActiveReadings, Initialization was needed', domoticz.LOG_INFO)
programActiveReadings.add(false)
end
local targetHum = domoticz.data['targetHum'..i]
if (targetHum == nil) then
domoticz.log('targetHum'..i..', Initialization was needed', domoticz.LOG_INFO)
domoticz.data['targetHum'..i] = 0
targetHum = 0
end
if (forceFanReadings.size == 0) then
domoticz.log('forceFanReadings, Initialization was needed', domoticz.LOG_INFO)
forceFanReadings.add('Init')
end
local programActiveState = programActiveReadings.getLatest()
if (programActiveState.data) then -- The fan control program is active
-- Has the fan control program timed out or have we reached the target humidity?
local maxTime = (programActiveState.time.minutesAgo > FAN_MAX_TIME)
local targetHumReached = (humidityReadings.getLatest().data <= targetHum)
if (maxTime or targetHumReached) then
domoticz.log('Dehumidification program stops for: '..HUMIDITY_SENSORS[i], domoticz.LOG_INFO)
domoticz.log('Reason(s): '..(maxTime and 'Max time. ' or '')..(targetHumReached and 'Target humidity reached.' or ''), domoticz.LOG_INFO)
programActiveReadings.add(false)
programActiveState = programActiveReadings.getLatest()
else
domoticz.log('Dehumidification program is active for: '..HUMIDITY_SENSORS[i], domoticz.LOG_INFO)
fanCmd = 'On'
end
else -- The fan is currently not running under the control of this program
-- Has there been a significant rise in humidity lately?
local humDelta = humidityReadings.getLatest().data - humidityReadings.min(2, READING_SAMPLES + 1)
-- Calculate a target humidity but never try to push humidity below 40
targetHum = math.max(humidityReadings.min(2, READING_SAMPLES + 1) + TARGET_OFFSET, 40)
if (humDelta > FAN_DELTA_TRIGGER and humidityReadings.getLatest().data > targetHum) then
domoticz.data['targetHum'..i] = targetHum
programActiveReadings.add(true)
programActiveState = programActiveReadings.getLatest()
fanCmd = 'On'
domoticz.log('Dehumidification program starts as a respond to: '..HUMIDITY_SENSORS[i], domoticz.LOG_INFO)
else
domoticz.log('Dehumidification program doesn\'t run for: '..HUMIDITY_SENSORS[i], domoticz.LOG_INFO)
end
domoticz.log('targetHum: '..targetHum..', Current humidity: '..humidityReadings.getLatest().data..', humDelta: '..humDelta, domoticz.LOG_INFO)
end
end
if ((forceFanReadings.getLatest().time.minutesAgo < FORCE_FAN_TIME)
and (forceFanReadings.getLatest().data == 'On')) then fanCmd = 'On' end
if (domoticz.devices(FAN_DEVICE).state ~= fanCmd) then
domoticz.log('Turning the fan '..fanCmd, domoticz.LOG_INFO)
domoticz.devices(FAN_DEVICE).toggleSwitch()
end
else
-- The script gets executed due to a device-change event
if (device.name == FORCE_FAN_DEVICE and device.state == 'On') then
forceFanReadings.add('On')
domoticz.log('The fan has been forced on for '..FORCE_FAN_TIME..'minutes.', domoticz.LOG_INFO)
-- domoticz.helpers.speak(domoticz, 'joke sting')
if (domoticz.devices(FAN_DEVICE).state ~= 'On') then domoticz.devices(FAN_DEVICE).switchOn() end
end
end
end
}
Change log and release notices
Version 1.3.1.
- Corrected an issue where the force button made the script run a faulty number of minutes.
Re: My dzVents Bathroom Humidity Control script
Posted: Wednesday 22 March 2017 18:48
by remb0
Hi BakseeDaa,
nice work!
I wanted to use your script because it's a lot cleaner, then my script.
But it's not easy to use in my case.. (and with my skills)
Some points i'm struggling with, and point you can maybe use:
-I use a virtual selector with 4 states. 3 speed modes and a max modus for 10 minute (this are the buttons of my remote)
this makes it more complicated because my fan is never off, but must go back to fanspeed 1
-the rise in hum is very nice.. But I also use: a temperature sensor that detects rising temperatures on a warm water pipe and when more then x min + somebody is home I am probably showering

- variables from the user variables, but with the new editor it is not needed anymore because it makes it a little bit more complicated
- a function for printing, logging to a csv so I can check in excel after a week when the script worked and how and why)
my script (also with components of others)
Code: Select all
--[[
....
]]--
--------------------------------------------------------------------------------
Script = 'Ventilatie'
Version = 1
VersionLog = 1
Man_or_Auto = 'Auto' -- Default
package.path = package.path .. ';' .. '/home/pi/domoticz/scripts/lua/?.lua'
My = require('MyFunc')
------- Functions--------------------------------------------------------------------------
function SendANotification()
commandArray['SendNotification']='Ventilatie ' .. Stand .. '#Ventilatie was in state ' .. Stand .. ' new state is ' .. NewState .. ', reason for change is ' .. My.EnumClear(Reason) .. ' / '.. os.date("%Y-%m-%d;%H:%M:%S").. '#0#pushover'
end
function LogActions()
LogFile=string.gsub(LogFile,"~d",os.date("%Y-%m-%d"))
LogFile=string.gsub(LogFile,"~m",os.date("%Y-%m"))
if not My.File_exists(LogFile) then
f=io.open(LogFile,"a")
f:write("Datum ; Tijd ; Man_or_Auto ; Stand ; Reason ; HumidityTmin10; HumidityTmin5; HumDelta; LeidingTmin10; LeidingTmin5; LeidingDelta; FanRuntime; FanAanVoor; target")
f:write("\r\n")
else
f=io.open(LogFile,"r")
c=f:read("*line")
f:close()
f=io.open(LogFile,"a")
end
f:write(os.date("%Y-%m-%d;%H:%M:%S") .. ";" .. Man_or_Auto .. ";" .. Stand .. ";" .. My.EnumClear(Reason) .. ";" .. HumidityTmin10 .. ";" .. HumidityTmin5 .. ";" .. HumDelta .. ";" .. LeidingTmin10 .. ";" .. LeidingTmin5 .. ";" .. LeidingDelta .. ";" .. FanRuntime .. ";" .. FanAanVoor .. ";" .. target)
f:write("\r\n")
f:close()
end
-- Functions
function Debug_msg (msg)
if (Debug == 'Y') then print('>> Fan: '.. msg) end
end
commandArray= {} -- default variables
UserVarErr = 0
Reason =""
LogFile = My.GetUserVar("FAN_LogFile") -- /home/pi/domoticz/log/FAN.csv
ShAfterAction = My.GetUserVar("FAN_ShAfterAction") -- sudo /home/pi/domoticz/scripts/bash/log-dropbox.sh
-- debugging
Debug = 'Y' -- 'Y' show prints Y of N
LogAction = My.GetUserVar("FAN_LogAction") -- 'A' A=All, Y=Only on change
-- offsets
target_OFFSET = 3 -- ventilator goes off if target + offset is reached
FAN_HumDelta_TRIGGER = 5 -- rise in humidity needed for a bath that will trigger the fan
targetOffHumidity = tonumber(uservariables['Fan_TargetOffHumidity']) + 0 -- target humidity (72)
WarmWaterTriggerTemp = tonumber(57) --42 temperature of hotwater pipe where fan should start running
WarmWaterLowerTrigger = tonumber(52) --33 temperature of hotwater pipe where fan should STOP running
-- Devices
IemandThuis = otherdevices['Iemand_thuis']
Fan = otherdevices['Fan']
LastChange_fan = otherdevices_lastupdate["Fan"]
Stand = tonumber(otherdevices_svalues['Fan'])/10 --Stand4 = otherdevices['Fan_4'] dit is een tijdelijke Stand 10 minuten maximaal
Badkamer = My.GetUserVar("FAN_HumBadkamer") -- 18.4 C, 73 % Hum is on position 2
warmwater = My.GetUserVar("FAN_Warmwater")
WaterleidingTemp= My.Round(tonumber(My.GetValue(otherdevices_svalues[warmwater],1)))
-- cycle instellingen (allemaal type=integer, value=0)
Fan_CycleInterval = 3 -- time in minutes when the script logic will happen
Counter = tonumber(uservariables['Fan_Counter'])+ 0
HumidityTmin5 = tonumber(uservariables['Fan_HumidityTmin5']) -- youngest reading
LeidingTmin5 = tonumber(uservariables['Fan_LeidingTmin5'])
HumidityTmin10 = tonumber(uservariables['Fan_HumidityTmin10']) -- oldest reading
LeidingTmin10 = tonumber(uservariables['Fan_LeidingTmin10'])
FanMaxRuntime = 10
FanRuntime = tonumber(uservariables['Fan_MaxRuntime']) --10 maximum amount of sample cycles the fan can be on, in case we never reach target humidity
FanAanVoor = My.Round(My.TimeDiff(os.time(),LastChange_fan)/60,0)
target = 0 -- will hold the target humidity when the program starts
-- als het if blok niet draait en er toch gelogd wordt
HumDelta = 0
LeidingDelta = 0
-- foutafhandeling
if UserVarErr> 0 then Debug_msg(". Please check user variables for ".. Script .. " script.") end
if UserVarErr> 0 then goto done end
-- get the CurrentHum humidity value and check if the sensor is on or has some weird reading
CurrentHum = otherdevices_humidity[Badkamer]
CurrentLeiding = tonumber(WaterleidingTemp)
if (CurrentHum == 0 or CurrentHum == nil) then print('CurrentHum is 0 or nil. Skipping this reading') return commandArray end
if (CurrentLeiding == 0 or CurrentLeiding == nil) then print('CurrentLeiding is 0 or nil. Skipping this reading') return commandArray end
Debug_msg("______________________________________________________________________")
Debug_msg(">> ".. Script .. ".......v" .. Version .. "...Debug = " .. Debug .. ' Run:' .. Counter .. '/' .. Fan_CycleInterval)
-- Checks en berekeningen die altijd worden uitgevoerd.
-- if (timeofday['Daytime']) then Res_Dayl=1 else Res_Dayl=0 Reason=Reason .. "buiten dagtijd, " end
if HumDelta > FAN_HumDelta_TRIGGER then Res_Hum_Badkamer=1 Reason=Reason .. "Badkamer vochtig: " .. HumidityTmin5 .. " ," else Res_Hum_Badkamer=0 end
if LeidingTmin10 > WarmWaterTriggerTemp and LeidingTmin5 > WarmWaterTriggerTemp then Res_Leiding=1 Reason=Reason .. "Leiding is warm: " .. WaterleidingTemp .. " ," else Res_Leiding=0 end
-- pick the lowest history value
HumDelta = tonumber(CurrentHum - math.min(HumidityTmin10, HumidityTmin5))
LeidingDelta = tonumber(CurrentLeiding - math.min(LeidingTmin10, LeidingTmin5))
target = math.min(HumidityTmin10, HumidityTmin5) + target_OFFSET
-- shift the previous measurements and store the CurrentHum
HumidityTmin10 = HumidityTmin5
HumidityTmin5 = CurrentHum
LeidingTmin10 = LeidingTmin5
LeidingTmin5 = CurrentLeiding
---LOGICA START, increase cycle Counter
Counter = Counter + 1
FanRuntime = FanRuntime +1
if (Counter >= Fan_CycleInterval) then
if (Stand == 0) then print('Fan is manueel, er wordt niets gedaan.') goto done end
if ((HumidityTmin5 == 0) or (LeidingTmin5 == 0)) then -- initialization, assume this is the first time
HumidityTmin5 = CurrentHum HumidityTmin10 = CurrentHum LeidingTmin5 = CurrentLeiding LeidingTmin10 = CurrentLeiding
end
Counter = 0 -- reset the cycle Counter. gaat alleen draaien als hij fan_cycleintervan haalt en mag daarna weer opnieuw beginnen.
-- leiding moet boven trigger zijn maar de delta moet er ook zijn..
if Res_Leiding == 1 then
Debug_msg('--someone is showering. ')
if (Stand == 1 ) then
commandArray['Fan']='Set Level: 30'
Debug_msg('Douche: van Stand 1 naar 3')
FanRuntime = 0
Reason=Reason .. "Waterleiding is: " .. WaterleidingTemp .. ", van stand 1>3," else
if(Stand == 2 ) then
commandArray['Fan']='Set Level: 30'
Debug_msg('Douche: van Stand 2>3')
FanRuntime = 0
Reason=Reason .. "Waterleiding is: " .. WaterleidingTemp .. ", van stand 2>3," else
if (Stand == 3 ) then
Debug_msg('Douche: op Stand 3 laten')
FanRuntime = FanRuntime + 1
Reason=Reason .. "Waterleiding is: " .. WaterleidingTemp .. ", van stand 3 blijft 3 ," else
if (Stand == 4) then
Debug_msg('staat al op Stand 4')
FanRuntime = FanRuntime + 1
Reason=Reason .. "Waterleiding is: " .. WaterleidingTemp .. ", Blijft stand 4 ," else
end
end
end
end
else
-- als de leidingtemperatuur onder de afkoelwaarde is.
if (Stand >= 2) and (WaterleidingTemp >= WarmWaterLowerTrigger) then
Debug_msg('<b style="color:Blue">Fan is naventilatie stand.</b>')
Reason=Reason .. ": Fan in afkoelstand, leiding is " .. WaterleidingTemp .. " en gaat uit als " .. WarmWaterLowerTrigger .. " bereikt is ,"
else
if (Stand >= 2) and (FanRuntime >= FanMaxRuntime) then
Debug_msg('<b style="color:Blue">Fan has exceeded max runtime (' .. FanMaxRuntime .. ' min), turn fan off</b>')
commandArray['Fan']='Set Level: 10'
FanRuntime = 0
Reason=Reason .. "Fan staat te lang aan: " .. FanRuntime .. " / " .. FanMaxRuntime .. " ,"
end
if (Stand >= 2) and (WaterleidingTemp <= WarmWaterLowerTrigger) then
Debug_msg('<b style="color:Blue">Leiding afgekoeld, Fan uit</b>')
commandArray['Fan']='Set Level: 10'
FanRuntime = 0
Reason=Reason .. "Leiding afgekoeld, Fan uit: " .. WaterleidingTemp .. " >= " .. WarmWaterLowerTrigger .. " ,"
end
end
end
-- op basis van Humidity
if (Stand >= 1 ) then
-- see if we have to turn it on for the shower
if (HumDelta >= FAN_HumDelta_TRIGGER) then
-- time to start the fan
commandArray['Fan']='Set Level: 30'
Reason=Reason .. "Fan aan, vochtigheid is gestegen met" .. HumDelta .." procent,"
targetOffHumidity = target
-- set the safety stop
FanRuntime = 0
if (Stand >= 2) then
Debug_msg('Ventilator was already on but we start the de-humidifying program')
end
end
else
if (Stand == 1) then -- not manually started
if (HumDelta >= FAN_HumDelta_TRIGGER) then
-- ok, there is another FAN_HumDelta_TRIGGER rise in humidity
-- when this happen we reset the FanMaxRuntime to a new count down
-- because we have to ventilate a bit longer due to the extra humidity
Debug_msg('Another large increase detected, resetting max timer. HumDelta: ' .. HumDelta)
--FanRuntime = FanMaxRuntime
end
-- first see if it can be turned off
if (CurrentHum <= targetOffHumidity or FanRuntime >= FanMaxRuntime) then
commandArray['Fan']='Set Level: 10'
if (FanAanVoor >= FanMaxRuntime and CurrentHum > targetOffHumidity) then
Debug_msg('target not reached but safety time-out is triggered.')
else
Debug_msg('target humidity reached')
end
Debug_msg('Turning off the ventilator')
targetOffHumidity = 0
FanRuntime = 0
commandArray['Fan']='Set Level: 10'
-- reset history in this case.. we start all over
-- Tmin10 is still in the 'ventilator=On'-zone
HumidityTmin10 = HumidityTmin5
else
-- we haven't reached the target yet
Debug_msg('Humidity HumDelta: ' .. HumDelta)
end
end
end
if Debug == 'Y' then
Debug_msg("Stand: " .. Stand .. " sinds " .. FanAanVoor .. ' Minuten, FanRuntime: ' .. FanRuntime)
Debug_msg("IemandThuis: " .. IemandThuis )
Debug_msg('Waterleiding is nu ' .. CurrentLeiding .. ' Graden, Fan gaat aan boven ' .. WarmWaterTriggerTemp .. ' en mag afkoelen tot ' .. WarmWaterLowerTrigger .. ' graden')
Debug_msg('HumDelta: ' .. HumDelta .. ' LeidingDelta: ' .. LeidingDelta)
Debug_msg('HumidityTmin5: ' .. HumidityTmin5 .. ' HumidityTmin10: ' .. HumidityTmin10 .. ' TargetOffHumidity: ' .. targetOffHumidity)
Debug_msg('LeidingTmin5: ' .. LeidingTmin5 .. ' LeidingTmin10: ' .. LeidingTmin10)
Debug_msg( "reden: " .. My.EnumClear(Reason))
end
end
-- save the globals
commandArray['Variable:Fan_Counter'] = tostring(Counter)
commandArray['Variable:Fan_HumidityTmin10'] = tostring(HumidityTmin10)
commandArray['Variable:Fan_HumidityTmin5'] = tostring(HumidityTmin5)
commandArray['Variable:Fan_LeidingTmin10'] = tostring(LeidingTmin10)
commandArray['Variable:Fan_LeidingTmin5'] = tostring(LeidingTmin5)
commandArray['Variable:Fan_targetOffHumidity'] = tostring(targetOffHumidity)
commandArray['Variable:Fan_MaxRuntime'] = tostring(FanRuntime)
if LogAction=='A' or ((LogAction=='C') and (LeidingDelta <= -2 or LeidingDelta >= 4 )) then LogActions() end
-- om de 1 uur naar log-dropbox
-- if (string.len(ShAfterAction)) > 1 then os.execute(ShAfterAction) end
::done::
Debug_msg("______________________________________________________________________")
return commandArray
Re: My dzVents Bathroom Humidity Control script
Posted: Thursday 23 March 2017 7:31
by BakSeeDaa
remb0 wrote:Hi BakseeDaa,
nice work!
I wanted to use your script because it's a lot cleaner, then my script.
But it's not easy to use in my case.. (and with my skills)
Some points i'm struggling with, and point you can maybe use:
...
That's a huge script @remb0
Many of the features are a bit beyond the scope of my script but I will consider to add a better logging function. I think it would also be nice to make it work with multiple fans.
Cheers!
Re: My dzVents Bathroom Humidity Control script
Posted: Thursday 23 March 2017 9:17
by dannybloe
BakSeeDaa wrote:Below is my dzVents Bathroom Humidity Control script version 1.0.0
I've been inspired by this script:
Lua script for controlling humidity in the bathroom and I got some design tips from
@dannybloe.
My house has a single fan that extracts air from all the rooms in the house. I can force that single fan to work at a higher speed in case of that the humidity in one of my bathrooms rises quickly. My script currently supports only a single fan but multiple humidity sensors. The script also supports a button that can be pushed manually in case that the air quality in the bathroom requires that. I may improve my script and update this first post if I get any suggestions for improvements from you guys. Please feel free to suggest improvements etc.
Very nice!
Re: My dzVents Bathroom Humidity Control script
Posted: Thursday 30 March 2017 17:07
by mcmikev
I love the script and want to use it but I require and override option.
I have a script running now and that checks if the override switch (dummy device with timer) is off before putting the fan in max mode.
This is needed because when i get home late I don't want to wake up the wife who is sleeping on the other side of the wall where the fan (central suction unit) is located because that makes a lot of noise at 100%.
I tried to look at the code to figure it out myself but I am not able to find where to put the "check if override = off" line.
Can someone help me out here please?
So when hum rises but override is on, then don't trigger fan switch.
Hope my story makes sense.
Keep up the great work!
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 31 March 2017 7:56
by BakSeeDaa
mcmikev wrote:I love the script and want to use it but I require and override option.
I have a script running now and that checks if the override switch (dummy device with timer) is off before putting the fan in max mode.
This is needed because when i get home late I don't want to wake up the wife who is sleeping on the other side of the wall where the fan (central suction unit) is located because that makes a lot of noise at 100%.
I tried to look at the code to figure it out myself but I am not able to find where to put the "check if override = off" line.
Can someone help me out here please?
So when hum rises but override is on, then don't trigger fan switch.
Hope my story makes sense.
Keep up the great work!
You can try something like the following. It has not been tested though but it should give you some idea...
Code: Select all
--[[
bathroomHumControl.lua by BakSeeDaa
Version 1.2.0- Customized
This script controls the humidity in a typical bathroom setting by detecting
relative rises in humidity in a short period.
--]]
local FAN_DEVICE = 'Nibe F750 Force Fan' -- Fan device
local FORCE_FAN_DEVICE = 'Flatulence Button' -- (Optional)
local BLOCK_FAN_DEVICE = 'XXXXXXXX' -- (Optional) When this device is on, it will block the FAN_DEVICE from turning on
local FORCE_FAN_TIME = 10 -- Minutes to force the fan when button pushed
local HUMIDITY_SENSORS = {'Badrum 1 C+RH', 'Badrum 2 C+RH'}
local FAN_DELTA_TRIGGER = 3 -- % Rise in humidity that will trigger the fan
local FAN_MAX_TIME = 24 -- Maximum minutes that the fan can be on in case we never reach the target humidity
local TARGET_OFFSET = 2 -- Fan goes off if target + offset is reached
local TEST_MODE_HUMIDITY_READING = 0 -- Set to a value between 1 and 100. Set to 0 to disable test mode
local READING_SAMPLES = 15
-- Create the data declarations
local data = {}
for i, device in pairs(HUMIDITY_SENSORS) do
data[device] = {history = true, maxItems = READING_SAMPLES + 1}
data['dehumidProgramActive'..i] = {history = true, maxItems = 1} -- Need history to get time stamp
data['forceFan'] = {history = true, maxItems = 1} -- Need history to get time stamp
data['targetHum'..i] = {initial=0}
end
return {
on = {
['timer'] = 'every 1 minutes',
FORCE_FAN_DEVICE
},
data = data,
active = true,
execute = function(domoticz, device, triggerInfo)
local LOG_LEVEL = domoticz.LOG_DEBUG -- Script default log level. You may change this.
local forceFanReadings = domoticz.data['forceFan']
if (triggerInfo.type == domoticz.EVENT_TYPE_TIMER) then
local fanCmd = 'Off'
for i = 1, #HUMIDITY_SENSORS do
local humidityReadings = domoticz.data[HUMIDITY_SENSORS[i]]
-- Store the read value in the persistant data
for j = 1, (humidityReadings.size == 0 and READING_SAMPLES + 1 or 1) do
humidityReadings.add((TEST_MODE_HUMIDITY_READING == 0
and domoticz.devices[HUMIDITY_SENSORS[i]].humidity or TEST_MODE_HUMIDITY_READING))
end
-- INIT
local programActiveReadings = domoticz.data['dehumidProgramActive'..i]
if (programActiveReadings.size == 0) then
domoticz.log('programActiveReadings, Initialization was needed', LOG_LEVEL)
programActiveReadings.add(false)
end
local targetHum = domoticz.data['targetHum'..i]
if (targetHum == nil) then
domoticz.log('targetHum'..i..', Initialization was needed', LOG_LEVEL)
domoticz.data['targetHum'..i] = 0
targetHum = 0
end
if (forceFanReadings.size == 0) then
domoticz.log('forceFanReadings, Initialization was needed', LOG_LEVEL)
forceFanReadings.add('Init')
end
programActiveState = programActiveReadings.getLatest()
if (programActiveState.data) then -- The fan control program is active
-- Has the fan control program timed out or have we reached the target humidity?
local maxTime = (programActiveState.time.minutesAgo > FAN_MAX_TIME)
local targetHumReached = (humidityReadings.getLatest().data <= targetHum)
if (maxTime or targetHumReached or (domoticz.devices[BLOCK_FAN_DEVICE].state == 'On')) then
domoticz.log('Dehumidification program stops for: '..HUMIDITY_SENSORS[i], LOG_LEVEL)
domoticz.log('Reason(s): '..(maxTime and 'Max time. ' or '')..(targetHumReached and 'Target humidity reached. ' or '')..((domoticz.devices[BLOCK_FAN_DEVICE].state == 'On') and 'Blocking device is on.' or ''), LOG_LEVEL)
programActiveReadings.add(false)
programActiveState = programActiveReadings.getLatest()
else
domoticz.log('Dehumidification program is active for: '..HUMIDITY_SENSORS[i], LOG_LEVEL)
fanCmd = 'On'
end
else -- The fan is currently not running under the control of this program
-- Has there been a significant rise in humidity lately?
local humDelta = humidityReadings.getLatest().data - humidityReadings.min(2, READING_SAMPLES + 1)
-- Calculate a target humidity but never try to push humidity below 40
targetHum = math.max(humidityReadings.min(2, READING_SAMPLES + 1) + TARGET_OFFSET, 40)
if (humDelta > FAN_DELTA_TRIGGER and humidityReadings.getLatest().data > targetHum) then
domoticz.data['targetHum'..i] = targetHum
programActiveReadings.add(true)
programActiveState = programActiveReadings.getLatest()
fanCmd = 'On'
domoticz.log('Dehumidification program starts as a respond to: '..HUMIDITY_SENSORS[i], LOG_LEVEL)
else
domoticz.log('Dehumidification program doesn\'t run for: '..HUMIDITY_SENSORS[i], LOG_LEVEL)
end
domoticz.log('targetHum: '..targetHum..', Current humidity: '..humidityReadings.getLatest().data..', humDelta: '..humDelta, LOG_LEVEL)
end
end
if ((forceFanReadings.getLatest().time.minutesAgo < FAN_MAX_TIME)
and (forceFanReadings.getLatest().data == 'On')) then fanCmd = 'On' end
if (domoticz.devices[FAN_DEVICE].state ~= fanCmd) then
domoticz.log('Turning the fan '..fanCmd, LOG_LEVEL)
domoticz.devices[FAN_DEVICE].toggleSwitch()
end
else
-- The script gets executed due to a device-change event
if (device.name == FORCE_FAN_DEVICE and device.state == 'On') then
forceFanReadings.add('On')
domoticz.log('The fan has been forced on for '..FORCE_FAN_TIME..'minutes.', LOG_LEVEL)
--domoticz.speak('joke sting')
if (domoticz.devices[FAN_DEVICE].state ~= 'On') then domoticz.devices[FAN_DEVICE].switchOn() end
end
end
end
}
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 31 March 2017 14:46
by gerard76
I like and am inspired by your scripts. My situation is a little bit different, but I have some stuff somebody else might be inspired by.
I wanted to know if venting was actually useful, because if there is more moist outside then what we have on the inside, its no use to vent. I added a "Weather Underground" device that gives me temp and humidity from outside, but this might as well be your own weather station. The code calculates the absolute moisture content based on temp and relative humidity because we can not use the relative values when there is a temperature difference. There is one unused function
dew_point but because these humidity functions will be extracted to a separate file in the future I have left it in.
The original code was too slow to react for me (max 10 minutes) so I sped things up by triggering the script by 'device' and check if the set interval (default 10 secs) has expired. In my situation the fan turns on in about 40 secs after I turn on the shower. My mirror stays clean this way.
I store a humidity measurement every interval in a user variable string and 'explode' and 'compact' this in a table to manipulate it. This saves creating a lot of variables with only 2 lines of code. The initial value of this string determines how many intervals are stored. A reasonable default is
58;58;58;58;58;58;58;58;58;58;58;58. This is 2 minutes worth of readings. If this is not enough for your situation you can make it longer or increase the
interval.
Because I have no central venting system in my house, the fan doubles as a dehumidifier for my top floor. When it is not running for the shower it makes sure relative humidity never exceeds
max_humidity.
Manual override (only off for now) is done by setting my local Z-Wave switch (parameter 20) for the fan to 'Toggle switch (contact closed - On, contact open - Off)', but I really want to add a window sensor so the fan will not turn on when the window is open. Trying to save energy after all...
Code: Select all
commandArray = {}
measurements = {}
-- adjust these:
fan_name = 'Badkamer ventilator'
inside_sensor = 'Badkamer'
outside_sensor = 'Buiten'
max_humidity = 58 -- max humidity in room
interval = 10 -- seconds between samples
max_run_time = 60*60 -- seconds
humidity_rise = 2 -- rise in humidity that will trigger the fan
--[[ create these user variables:
target_humidity = 58 (integer)
humidity_over_time = "58;58;58;58;58;58;58;58;58;58;58;58" (string)
--]]
inside_hum = tonumber(otherdevices_humidity[inside_sensor])
inside_temp = tonumber(otherdevices_temperature[inside_sensor])
outside_temp = tonumber(otherdevices_temperature[outside_sensor])
outside_hum = tonumber(otherdevices_humidity[outside_sensor])
function dew_point(temp, hum)
local a = math.log(hum / 100) + (17.62 * temp / (243.12 + temp))
return 243.12 * a / (17.62 - a)
end
function saturation_pressure(temp)
local a = 7.5
local b = 237.3
if (temp < 0) then
a = 7.6
b = 240.7
end
return (6.1078 * math.exp(math.log(10) * ((a * temp) / (b + temp))))
end
function vapor_pressure(temp, hum)
return (hum / 100 * saturation_pressure(temp))
end
function abs_humidity(temp, hum)
local tk = temp + 273.15
return (math.exp(math.log(10) * 5) * 18.016/8314.3 * vapor_pressure(temp, hum)/tk)
end
-- returns seconds elapsed since timestamp
function seconds_since(timestamp)
y, m, d, H, M, S = timestamp:match("(%d+)-(%d+)-(%d+) (%d+):(%d+):(%d+)")
return os.difftime (os.time(), os.time{year=y, month=m, day=d, hour=H, min=M, sec=S})
end
if seconds_since(uservariables_lastupdate['humidity_over_time']) >= interval then
fan_on = otherdevices[fan_name] == "On"
target = uservariables['target_humidity']
-- put all previous readings in measurements table
uservariables['humidity_over_time']:gsub("(%d+)", function(c) table.insert(measurements,c) end)
table.remove(measurements) -- pop oldest value
table.insert(measurements, 1, inside_hum) -- push new value
commandArray['Variable:humidity_over_time'] = table.concat(measurements, ';')
if not fan_on then
min_hum = math.min(table.unpack(measurements))
if inside_hum - min_hum >= humidity_rise then
-- shower is on
target = min_hum
end
if uservariables['target_humidity'] ~= tonumber(target) then
-- we have a new target
commandArray['Variable:target_humidity'] = tostring(target)
end
end
too_moist = inside_hum > target
useful = abs_humidity(inside_temp, inside_hum) > abs_humidity(outside_temp, outside_hum)
on_too_long = fan_on and seconds_since(otherdevices_lastupdate[fan_name]) > max_run_time
if not fan_on and too_moist and useful then
commandArray[fan_name] = "On"
elseif fan_on and (not useful or not too_moist or on_too_long) then
commandArray[fan_name] = "Off"
commandArray['Variable:target_humidity'] = tostring(max_humidity)
end
end
return commandArray
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 07 July 2017 15:34
by mcmikev
Right now after changing all the things to the new formats for version 2.0 I cannot get it to work.
Compar with nil keeps showing up.
Here is my script. Maybe someone can see what I don't see.
Code: Select all
-- assumptions:
-- the setpoint is set by a selector dummy device where the values are numeric temperatures
-- but you can easily change it to a setpoint device
local BOILER_DEVICE = 'State-CV-WK' -- switch device
local SETPOINT_DEVICE = 'Thermostaat WK' -- selector dummy device
local TEMPERATURE_SENSOR = 'TH-Woonkamer'
local HYSTERESIS = 0.1 -- temp has to drop this value below setpoint before boiler is on again
local SMOOTH_FACTOR = 3
local LOGGING = false
local OVERRIDE_SWITCH = 'CV-WK-Override' -- override switch
return {
on = {
timer = {
'every minute',
TEMPERATURE_SENSOR,
SETPOINT_DEVICE
}
},
data = {
temperatureReadings = { history = true, maxItems = SMOOTH_FACTOR }
},
active = true,
execute = function(domoticz, device, triggerInfo)
local avgTemp
local temperatureReadings = domoticz.data.temperatureReadings
-- first check if the sensor got a new reading or the setpoint was changed:
if (triggerInfo.type == domoticz.EVENT_TYPE_DEVICE) then
local sensor = domoticz.devices(TEMPERATURE_SENSOR)
if (sensor.changed and sensor.attributeChanged('temperature')) then
-- sensor just reported a new reading
-- add it to the readings table
local current = sensor.temperature
if (current ~= 0 and current ~= nil) then
temperatureReadings.add(current)
else
-- no need to be here, weird state detected
domoticz.log('Strange sensor reading.. skiping', domoticz.LOG_ERROR)
return
end
elseif (domoticz.devices(SETPOINT_DEVICE).changed) then
-- a new setpoint was set
if LOGGING then domoticz.log('Setpoint was set to ' .. device.state) end
else
-- no business here, bail out...
return
end
end
-- now determine what to do
local boiler = domoticz.devices(BOILER_DEVICE)
local setpoint = domoticz.devices(SETPOINT_DEVICE)
local override = domoticz.devices(OVERRIDE_SWITCH)
if (setpoint.state == nil or setpoint.state == 'Off') then
boiler.switchOff()
return -- we're done here
end
local setpointValue = tonumber(setpoint.state)
-- determine at which temperature the boiler should be
-- switched on
local switchOnTemp = setpointValue - HYSTERESIS
-- don't use the current reading but average it out over
-- the past <SMOOTH_FACTOR> readings (data smoothing) to get rid of noise, wrong readings etc
local avgTemp = temperatureReadings.avg(1, SMOOTH_FACTOR)
if LOGGING then
domoticz.log('Average: ' .. avgTemp, domoticz.LOG_INFO)
domoticz.log('Setpoint: ' .. setpointValue, domoticz.LOG_INFO)
domoticz.log('Current boiler state: ' .. boiler.state, domoticz.LOG_INFO)
domoticz.log('Switch-on temperature: ' .. switchOnTemp, domoticz.LOG_INFO)
end
if (avgTemp >= setpointValue and boiler.state == 'On') then
if LOGGING then domoticz.log('Target temperature reached, boiler off') end
boiler.switchOff()
boiler.switchOff().afterMin(2)
end
if (avgTemp < setpointValue and boiler.state == 'Off' and override.state == 'Off') then
if (avgTemp < switchOnTemp) then
if LOGGING then domoticz.log('Heating is required, boiler switched on') end
boiler.switchOn()
boiler.switchOn().afterMin(2)
else
if LOGGING then domoticz.log('Average temperature below setpoint but within hysteresis range, waiting for temperature to drop to ' .. switchOnTemp) end
end
end
if (boiler.state == 'On' and override.state == 'On') then
if LOGGING then domoticz.log('Override is aan, CV Woonkamer UIT') end
boiler.switchOff()
boiler.switchOff().afterMin(2)
end
end
}
the error
Code: Select all
2017-07-07 15:34:00.889 dzVents: Info: ------ Start internal script: dz_cv_woonkamer:, trigger: every minute
2017-07-07 15:34:00.921 dzVents: Debug: Device-adapter found for State-CV-WK: Switch device adapter
2017-07-07 15:34:00.934 dzVents: Debug: Processing device-adapter for State-CV-WK: Switch device adapter
2017-07-07 15:34:00.934 dzVents: Debug: Device-adapter found for Thermostaat WK: Thermostat setpoint device adapter
2017-07-07 15:34:00.935 dzVents: Debug: Processing device-adapter for Thermostaat WK: Thermostat setpoint device adapter
2017-07-07 15:34:00.935 dzVents: Debug: Device-adapter found for CV-WK-Override: Switch device adapter
2017-07-07 15:34:00.935 dzVents: Debug: Processing device-adapter for CV-WK-Override: Switch device adapter
2017-07-07 15:34:00.936 Error: dzVents: Error: An error occured when calling event handler dz_cv_woonkamer
2017-07-07 15:34:00.936 Error: dzVents: Error: ...cz/scripts/dzVents/generated_scripts/dz_cv_woonkamer.lua:87: attempt to compare number with nil
2017-07-07 15:34:00.936 dzVents: Info: ------ Finished dz_cv_woonkamer
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 07 July 2017 16:40
by randytsuch
So this is line 87
if (avgTemp >= setpointValue and boiler.state == 'On') then
I didn't try to figure out why it doesn't like this line.
If it was me, I'd split it into two separate if statements, and see which one it complains about. Then you "just" have to figure why.
Unless you already know which variable is generating the error?
One other thing, although it doesn't seem to be causing your problem
I could not do this
local temperatureReadings = domoticz.data.temperatureReadings
I had to use domoticz.data.variablename everywhere to make my script work. I haven't gone back yet to verify this was really the problem, but it works fine now, and that's what I did.
Randy
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 07 July 2017 17:08
by dannybloe
randytsuch wrote:So this is line 87
One other thing, although it doesn't seem to be causing your problem
I could not do this
local temperatureReadings = domoticz.data.temperatureReadings
That is strange as this should definitely work. I'll look into it.
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 07 July 2017 19:33
by mcmikev
I see that I posted some other issue in this place. OEPS.
That code is for turning heating on and off and not the humidity control .
I enabled this script in the event editor in domoticz and I see it running and it seems to be working but the fan keeps running for ever.
maybe you can see if all the new version 2.0 stuff is in there and is looks fine?
Code: Select all
-- This script controls the humidity in a typical bathroom setting by detecting
-- relative rises in humidity in a short period.
local FAN_DEVICE = 'State-AfzuigingBadkamer' -- Fan device
local FORCE_FAN_DEVICE = '$DIM-Badkamer-R2x Status' -- (Optional)
local FORCE_FAN_TIME = 10 -- Minutes to force the fan when button pushed
local AFZUIGING_OVERRIDE = 'Afzuiging-Override' -- (Optional) When this device is on, it will block the FAN_DEVICE from turning on
local HUMIDITY_SENSORS = {'TH-Badkamer'}
local FAN_DELTA_TRIGGER = 2 -- % Rise in humidity that will trigger the fan
local FAN_MAX_TIME = 60 -- Maximum minutes that the fan can be on in case we never reach the target humidity
local TARGET_OFFSET = 2 -- Fan goes off if target + offset is reached
local TEST_MODE_HUMIDITY_READING = 0 -- Set to a value between 1 and 100. Set to 0 to disable test mode
local DEBUG = true
local READING_SAMPLES = 15
-- Create the data declarations
local data = {}
for i, device in pairs(HUMIDITY_SENSORS) do
data[device] = {history = true, maxItems = READING_SAMPLES + 1}
data['dehumidProgramActive'..i] = {history = true, maxItems = 1} -- Need history to get time stamp
data['forceFan'] = {history = true, maxItems = 1} -- Need history to get time stamp
data['targetHum'..i] = {initial=0}
end
return {
on = {
timer = {
'every 1 minutes',
FORCE_FAN_DEVICE
}
},
data = data,
active = true,
execute = function(domoticz, device, triggerInfo)
local forceFanReadings = domoticz.data['forceFan']
if (triggerInfo.type == domoticz.EVENT_TYPE_TIMER) then
local fanCmd = 'Off'
for i = 1, #HUMIDITY_SENSORS do
local humidityReadings = domoticz.data[HUMIDITY_SENSORS[i]]
-- Store the read value in the persistant data
for j = 1, (humidityReadings.size == 0 and READING_SAMPLES + 1 or 1) do
humidityReadings.add((TEST_MODE_HUMIDITY_READING == 0
and domoticz.devices(HUMIDITY_SENSORS[i]).humidity or TEST_MODE_HUMIDITY_READING))
end
-- INIT
local programActiveReadings = domoticz.data['dehumidProgramActive'..i]
if (programActiveReadings.size == 0) then
print('programActiveReadings, Initialization was needed')
programActiveReadings.add(false)
end
local targetHum = domoticz.data['targetHum'..i]
if (targetHum == nil) then
print('targetHum'..i..', Initialization was needed')
domoticz.data['targetHum'..i] = 0
targetHum = 0
end
if (forceFanReadings.size == 0) then
print('forceFanReadings, Initialization was needed')
forceFanReadings.add('Init')
end
programActiveState = programActiveReadings.getLatest()
if (programActiveState.data) then -- The fan control program is active
-- Has the fan control program timed out or have we reached the target humidity?
local maxTime = (programActiveState.time.minutesAgo > FAN_MAX_TIME)
local targetHumReached = (humidityReadings.getLatest().data <= targetHum)
if (maxTime or targetHumReached or (domoticz.devices(AFZUIGING_OVERRIDE).state == 'On')) then
print('Dehumidification program stops for: '..HUMIDITY_SENSORS[i])
domoticz.log('Reason(s): '..(maxTime and 'Max time. ' or '')..(targetHumReached and 'Target humidity reached. ' or '')..((domoticz.devices(AFZUIGING_OVERRIDE).state == 'On') and 'Afzuiging Override is on.' or ''), LOG_LEVEL)
programActiveReadings.add(false)
programActiveState = programActiveReadings.getLatest()
else
print('Dehumidification program is active for: '..HUMIDITY_SENSORS[i])
fanCmd = 'On'
end
else -- The fan is currently not running under the control of this program
-- Has there been a significant rise in humidity lately?
local humDelta = humidityReadings.getLatest().data - humidityReadings.min(2, READING_SAMPLES + 1)
-- Calculate a target humidity but never try to push humidity below 40
targetHum = math.max(humidityReadings.min(2, READING_SAMPLES + 1) + TARGET_OFFSET, 40)
if (humDelta > FAN_DELTA_TRIGGER and humidityReadings.getLatest().data > targetHum) then
domoticz.data['targetHum'..i] = targetHum
programActiveReadings.add(true)
programActiveState = programActiveReadings.getLatest()
fanCmd = 'On'
if (DEBUG) then
print('Dehumidification program starts as a respond to: '..HUMIDITY_SENSORS[i])
end
else
if (DEBUG) then print('Dehumidification program doesn\'t run for: '..HUMIDITY_SENSORS[i]) end
end
if (DEBUG) then print('targetHum: '..targetHum..', Current humidity: '..humidityReadings.getLatest().data..', humDelta: '..humDelta) end
end
end
if ((forceFanReadings.getLatest().time.minutesAgo < FAN_MAX_TIME)
and (forceFanReadings.getLatest().data == 'On')) then fanCmd = 'On' end
if (domoticz.devices(FAN_DEVICE).state ~= fanCmd) then
if (DEBUG) then print('Turning the fan '..fanCmd) end
domoticz.devices(FAN_DEVICE).toggleSwitch()
end
else
-- The script gets executed due to a device-change event
if (device.name == FORCE_FAN_DEVICE and device.state == 'On') then
forceFanReadings.add('On')
print('The fan has been forced on for '..FORCE_FAN_TIME..'minutes.')
-- domoticz.speak('joke sting')
if (domoticz.devices(FAN_DEVICE).state ~= 'On') then domoticz.devices(FAN_DEVICE).switchOn() end
end
end
end
}
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 07 July 2017 19:40
by dannybloe
Pff, that's a complex script. Unfortunately I don't have time to fully understand it and analyse it. Sorry.
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 07 July 2017 19:42
by mcmikev
Hi, thx for the response.
The other code for the heating does not work for me at least.
I get compare to nil. I can't seem to find out why.
Maybe you see why?
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 07 July 2017 19:46
by dannybloe
I looks like you on-section is wrong, You combine timer and device triggers in the timer sub-section. Try this:
Code: Select all
return {
on = {
timer = { 'every minute'},
device = { TEMPERATURE_SENSOR, SETPOINT_DEVICE}
}
...
}
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 07 July 2017 19:57
by mcmikev
It seems to not find the average anymore.
I editted the code with your suggestion but still the same error
Code: Select all
2017-07-07 19:56:01.324 dzVents: Info: ------ Start internal script: dz_cv_badkamer:, trigger: every minute
2017-07-07 19:56:01.326 Error: dzVents: Error: An error occured when calling event handler dz_cv_badkamer
2017-07-07 19:56:01.326 Error: dzVents: Error: ...icz/scripts/dzVents/generated_scripts/dz_cv_badkamer.lua:83: attempt to compare number with nil
2017-07-07 19:56:01.326 dzVents: Info: ------ Finished dz_cv_badkamer
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 07 July 2017 20:03
by dannybloe
Can you try to add a default to the avg() method as when there is no data yet it returns nil:
Code: Select all
local avgTemp = temperatureReadings.avg(1, SMOOTH_FACTOR, 0) -- when there's no average, return 0 (or whatever you want it to return)
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 07 July 2017 20:08
by mcmikev
no error anymore but keeps turning heating on even though the setpoint is lower then temp measured.
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 07 July 2017 20:11
by mcmikev
the average is 0 now but that should be the temo average.
BTW: the script is from the example folder (1.x version) and editted. (simple room heating with hysteresis control)
So maybe you can see what the issue is with the temp reading?
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 07 July 2017 20:19
by dannybloe
Yes, just dawned to me that it really resembles my own heating script that I used a while ago. Hahaha.
So, as I have been running that script for quite some time it should work (famous last words).
Anyway,
Code: Select all
boiler.switchOff()
boiler.switchOff().afterMin(2)
This looks weird. I don't know what Domoticz will do with these two commands.
But, what I would do is put print statements in the various locations and see if you get to the proper if-branches and what the values are. And I'd get rid of those weird double switch commands.
Re: My dzVents Bathroom Humidity Control script
Posted: Friday 07 July 2017 20:23
by mcmikev
Hi all, seems like i hijacked this topic. I created a topic for the issue.
https://www.domoticz.com/forum/viewtop ... 8#p140658