'Generic' means generic in my house Some tuning might be necessary depending on the situation.
Functionality per shutter:
- partly open when it's light and after a specific time per day
- open completely when it's light and after a specific time per day
- close when it's dark or at a specific time per day
- partly close on a bright day when the sun shines in my window
- keep the shutter closed when it really cold outside
- close the shutter when it's very hot and when it's cooler inside then outside
- open window detection (Do nothing while window is open)
- dummy switch can be used to set the shutter to manual mode
- dummy text device can be used to display the state of the shutter
- manual operation detection (Operated by wall switch or manually in Domoticz/HomeKit/...) In this case automation is suspended until Day->Night, Night->Day change or when manual mode switch is turned off
All functionalities can be enabled and configures per shutter.
Dependencies:
- Domoticz-SunMoon-Plugin
- variable DayNight which is set in another script. Possible values are defined in global_data constants.
- global_data entries:
CONST_DAGNACHT_DAG = 'DAG'
CONST_DAGNACHT_NACHT = 'NACHT'
SUN = 1 -- dz.time.wday returns 1 for sunday
MON = 2
TUE = 3
WED = 4
THU = 5
FRI = 6
SAT = 7
- All my device idx's are defined in my global_data, but also the IDXes numbers can be used.
At this moment the script runs without any problems. But it can always be improved. Every feedback is appreciated.
Code: Select all
-- Version: 1.3
-- Date: 04-05-2920
-- changelog
-- 1.0 initial Version
-- 1.1 for open en partly open use sunday time on public holidays
-- 1.2 idx_sunAltitude and idx_sunAzimuth are made optional
-- for close nighttime use Saturday time for night before public holiday
-- 1.3 Added switch for sleep late
-- 1.4 Added sunscreen functionality
local idx_outsideTemp = idx_achtertuin_thb -- idx for outside temperasture
local idx_wind = idx_achtertuin_wind -- idx for wind sensor
local idx_rain = idx_achtertuin_regen -- idx for rain sensor
local idx_sunAltitude = idx_sunmoon_sunAltitude -- idx for sunAltitude device (Domoticz-SunMoon plugin), nil = don't use
local idx_sunAzimuth = idx_sunmoon_sunAzimuth -- idx for sunAzimuth device (Domoticz-SunMoon plugin), nil = don't use
local idxv_dagNacht = var_idx_dagNacht -- idx for Day/Night variable
local dumpDeviceConfigToLog = false -- set true to dump device config to log. Normally false
local WINDOW_SENSOR_VALID_TIME = 360 --minutes
local TEMP_SENSOR_VALID_TIME = 120 --minutes
local WIND_SENSOR_VALID_TIME = 90 --minutes
local RAIN_SENSOR_VALID_TIME = 90 --minutes
local COMMAND_EXECUTE_TIME = 90 --seconds
SHUTTERS = {
{
name = 'Woonkamer', -- Name of the shutter. Just for logging.
enabled = true, -- enable/disable controll of this shutter
-- devices
idx_controller = idx_woonkamer_rolluik, -- idx of shutter device
idx_manualModeSwitch = idx_woonkamer_rolluikManualMode, -- idx of virtual switch. On = manual mode
idx_windowSensor = nil, -- idx of window sensor. Can be nil
idx_roomTemp = idx_woonkamer_thb, -- idx of room temperature sensor
idx_status = idx_woonkamer_rolluikStatus, -- idx of virtual text device for status messages
idx_luxSensor_down = idx_omgeving_lux_voorkant_avg, -- idx of lux sensor for down (for example a dummy device with avg value of a lux sensor)
idx_luxSensor_up = idx_omgeving_lux_voorkant_max, -- idx of lux sensor for up (for example a dummy device with max value of a lux sensor)
idx_sleepLate = nil, -- idx of virtual switch to indicate you want to sleep late tomorrow. Can be nil
-- settings
supportsPercentage = true, -- if true setpoints can be from 0 to 100, when false only up/down (>= 50 is up)
manualOperatedDetection = true, -- manual operation detection doesn't work for all shutters
partlyOpenAtDaytime = true,
partlyOpenPercentage = 30,
openAtDaytime = true,
closeAtNightTime = true,
openAtNightTime = false,
partlyCloseWhenSunlight = true,
minSunAltitude = 0,
maxSunAltitude = 75,
minSunAzimuth = 30,
maxSunAzimuth = 170,
luxThresholdDown = 45000,
luxThresholdUp = 30000,
partlyCloseSunlightPercentage = 35,
closeWhenColdOutside = false,
closeWhenColdOutsideThreshold = 20,
closeWhenHotOutside = true,
closeWhenHotOutsideThreshold = 25,
sleepLateClosedUntil = nil,
sleepLatePartlyClosedUntil = nil,
openWhenWind = false,
maxWindSpeed = 40,
openWhenRain = false,
maxRain = 0.1,
commandDelay = 0, -- ms
closedUntil = {
[MON] = '07:00',
[TUE] = '07:00',
[WED] = '07:00',
[THU] = '07:00',
[FRI] = '07:00',
[SAT] = '08:00',
[SUN] = '08:00'
},
partlyClosedUntil = {
[MON] = '08:30',
[TUE] = '08:30',
[WED] = '08:30',
[THU] = '08:30',
[FRI] = '08:30',
[SAT] = '09:00',
[SUN] = '10:00'
},
closeTime = {
[MON] = nil,
[TUE] = nil,
[WED] = nil,
[THU] = nil,
[FRI] = nil,
[SAT] = nil,
[SUN] = nil
},
},
{
name = 'Screen achter',
enabled = true,
-- devices
idx_controller = idx_woonkamer_screenAchter,
idx_manualModeSwitch = idx_woonkamer_screenAchterManualMode,
idx_windowSensor = nil,
idx_roomTemp = nil,
idx_status = idx_woonkamer_screenAchterStatus,
idx_luxSensor_down = idx_omgeving_lux_achterkant_avg,
idx_luxSensor_up = idx_omgeving_lux_achterkant_max,
idx_sleepLate = nil,
-- settings
supportsPercentage = false,
manualOperatedDetection = true,
partlyOpenAtDaytime = false,
partlyOpenPercentage = 0,
openAtDaytime = true,
closeAtNightTime = false,
openAtNightTime = true,
partlyCloseWhenSunlight = true,
minSunAltitude = 0,
maxSunAltitude = 75,
minSunAzimuth = 30,
maxSunAzimuth = 170,
luxThresholdDown = 35000,
luxThresholdUp = 20000,
partlyCloseSunlightPercentage = 0,
closeWhenColdOutside = false,
closeWhenColdOutsideThreshold = 20,
closeWhenHotOutside = false,
closeWhenHotOutsideThreshold = 25,
sleepLateClosedUntil = nil,
sleepLatePartlyClosedUntil = nil,
openWhenWind = true,
maxWindSpeed = 40,
openWhenRain = true,
maxRain = 0.1,
commandDelay = 0, -- ms
closedUntil = {
[MON] = '00:00',
[TUE] = '00:00',
[WED] = '00:00',
[THU] = '00:00',
[FRI] = '00:00',
[SAT] = '00:00',
[SUN] = '00:00'
},
partlyClosedUntil = {
[MON] = nil,
[TUE] = nil,
[WED] = nil,
[THU] = nil,
[FRI] = nil,
[SAT] = nil,
[SUN] = nil
},
closeTime = {
[MON] = nil,
[TUE] = nil,
[WED] = nil,
[THU] = nil,
[FRI] = nil,
[SAT] = nil,
[SUN] = nil
},
},
{
name = 'Screen zijkant',
enabled = true,
-- devices
idx_controller = idx_woonkamer_screenZijkant,
idx_manualModeSwitch = idx_woonkamer_screenZijkantManualMode,
idx_windowSensor = nil,
idx_roomTemp = nil,
idx_status = idx_woonkamer_screenZijkantStatus,
idx_luxSensor_down = idx_omgeving_lux_zijkant_avg,
idx_luxSensor_up = idx_omgeving_lux_zijkant_max,
idx_sleepLate = nil,
-- settings
supportsPercentage = false,
manualOperatedDetection = true,
partlyOpenAtDaytime = false,
partlyOpenPercentage = 0,
openAtDaytime = true,
closeAtNightTime = false,
openAtNightTime = true,
partlyCloseWhenSunlight = true,
minSunAltitude = 0,
maxSunAltitude = 75,
minSunAzimuth = 30,
maxSunAzimuth = 170,
luxThresholdDown = 35000,
luxThresholdUp = 20000,
partlyCloseSunlightPercentage = 0,
closeWhenColdOutside = false,
closeWhenColdOutsideThreshold = 20,
closeWhenHotOutside = false,
closeWhenHotOutsideThreshold = 25,
sleepLateClosedUntil = nil,
sleepLatePartlyClosedUntil = nil,
openWhenWind = true,
maxWindSpeed = 40,
openWhenRain = true,
maxRain = 0.1,
commandDelay = 0, -- ms
closedUntil = {
[MON] = '00:00',
[TUE] = '00:00',
[WED] = '00:00',
[THU] = '00:00',
[FRI] = '00:00',
[SAT] = '00:00',
[SUN] = '00:00'
},
partlyClosedUntil = {
[MON] = nil,
[TUE] = nil,
[WED] = nil,
[THU] = nil,
[FRI] = nil,
[SAT] = nil,
[SUN] = nil
},
closeTime = {
[MON] = nil,
[TUE] = nil,
[WED] = nil,
[THU] = nil,
[FRI] = nil,
[SAT] = nil,
[SUN] = nil
},
},
{
name = 'Slaapkamer',
enabled = true,
-- devices
idx_controller = idx_slaapkamer_rolluik,
idx_manualModeSwitch = idx_slaapkamer_rolluikManualMode,
idx_windowSensor = idx_slaapkamer_raam,
idx_roomTemp = idx_slaapkamer_thb,
idx_status = idx_slaapkamer_rolluikStatus,
idx_luxSensor_down = idx_omgeving_lux_achterkantBoven_avg,
idx_luxSensor_up = idx_omgeving_lux_achterkantBoven_max,
idx_sleepLate = idx_slaapkamer_uitslapen,
-- settings
supportsPercentage = true,
manualOperatedDetection = true,
partlyOpenAtDaytime = true,
partlyOpenPercentage = 35,
openAtDaytime = true,
closeAtNightTime = true,
openAtNightTime = false,
partlyCloseWhenSunlight = true,
minSunAltitude = 0,
maxSunAltitude = 90,
minSunAzimuth = 210,
maxSunAzimuth = 350,
luxThresholdDown = 30000,
luxThresholdUp = 15000,
partlyCloseSunlightPercentage = 35,
closeWhenColdOutside = true,
closeWhenColdOutsideThreshold = 0,
closeWhenHotOutside = true,
closeWhenHotOutsideThreshold = 24,
sleepLateClosedUntil = '10:30',
sleepLatePartlyClosedUntil = '11:00',
openWhenWind = false,
maxWindSpeed = 40,
openWhenRain = false,
maxRain = 0.1,
commandDelay = 200, -- ms
closedUntil = {
[MON] = '07:00',
[TUE] = '07:00',
[WED] = '07:00',
[THU] = '07:00',
[FRI] = '07:00',
[SAT] = '08:00',
[SUN] = '09:00'
},
partlyClosedUntil = {
[MON] = '08:30',
[TUE] = '08:30',
[WED] = '08:30',
[THU] = '08:30',
[FRI] = '08:30',
[SAT] = '09:00',
[SUN] = '10:30'
},
closeTime = {
[MON] = nil,
[TUE] = nil,
[WED] = nil,
[THU] = nil,
[FRI] = nil,
[SAT] = nil,
[SUN] = nil
},
},
{
name = 'Floris',
enabled = true,
-- devices
idx_controller = idx_floris_rolluik,
idx_manualModeSwitch = idx_floris_rolluikManualMode,
idx_windowSensor = idx_floris_raam,
idx_roomTemp = idx_floris_thb,
idx_status = idx_floris_rolluikStatus,
idx_luxSensor_down = idx_omgeving_lux_voorkant_avg,
idx_luxSensor_up = idx_omgeving_lux_voorkant_max,
idx_sleepLate = idx_floris_uitslapen,
-- settings
supportsPercentage = true,
manualOperatedDetection = true, -- manual operation detection doesn't work for all shutters
partlyOpenAtDaytime = true,
partlyOpenPercentage = 35,
openAtDaytime = true,
openAtNightTime = false,
closeAtNightTime = true,
partlyCloseWhenSunlight = true,
minSunAltitude = 10,
maxSunAltitude = 75,
minSunAzimuth = 30,
maxSunAzimuth = 170,
luxThresholdDown = 30000,
luxThresholdUp = 15000,
partlyCloseSunlightPercentage = 35,
closeWhenColdOutside = true,
closeWhenColdOutsideThreshold = 0,
closeWhenHotOutside = true,
closeWhenHotOutsideThreshold = 24,
sleepLateClosedUntil = '10:30',
sleepLatePartlyClosedUntil = '11:00',
openWhenWind = false,
maxWindSpeed = 40,
openWhenRain = false,
maxRain = 0.1,
commandDelay = 400, -- ms
closedUntil = {
[MON] = '07:00',
[TUE] = '07:00',
[WED] = '07:00',
[THU] = '07:00',
[FRI] = '07:00',
[SAT] = '09:00',
[SUN] = '09:00'
},
partlyClosedUntil = {
[MON] = '08:30',
[TUE] = '08:30',
[WED] = '08:30',
[THU] = '08:30',
[FRI] = '08:30',
[SAT] = '09:30',
[SUN] = '10:30'
},
closeTime = {
[MON] = '19:45',
[TUE] = '19:45',
[WED] = '19:45',
[THU] = '19:45',
[FRI] = '20:15',
[SAT] = '20:15',
[SUN] = '19:45'
},
},
{
name = 'Olivier',
enabled = true,
-- devices
idx_controller = idx_olivier_rolluik,
idx_manualModeSwitch = idx_olivier_rolluikManualMode,
idx_windowSensor = nil,
idx_roomTemp = idx_olivier_thb,
idx_status = idx_olivier_rolluikStatus,
idx_luxSensor_down = idx_omgeving_lux_voorkant_avg,
idx_luxSensor_up = idx_omgeving_lux_voorkant_max,
idx_sleepLate = idx_olivier_uitslapen,
-- settings
supportsPercentage = true,
manualOperatedDetection = true, -- manual operation detection doesn't work for all shutters
partlyOpenAtDaytime = true,
partlyOpenPercentage = 35,
openAtDaytime = true,
closeAtNightTime = true,
openAtNightTime = false,
partlyCloseWhenSunlight = true,
minSunAltitude = 10,
maxSunAltitude = 75,
minSunAzimuth = 30,
maxSunAzimuth = 170,
luxThresholdDown = 30000,
luxThresholdUp = 15000,
partlyCloseSunlightPercentage = 35,
closeWhenColdOutside = true,
closeWhenColdOutsideThreshold = 0,
closeWhenHotOutside = true,
closeWhenHotOutsideThreshold = 24,
sleepLateClosedUntil = '10:30',
sleepLatePartlyClosedUntil = '11:00',
openWhenWind = false,
maxWindSpeed = 40,
openWhenRain = false,
maxRain = 0.1,
commandDelay = 600, -- ms
closedUntil = {
[MON] = '07:00',
[TUE] = '07:00',
[WED] = '07:00',
[THU] = '07:00',
[FRI] = '07:00',
[SAT] = '08:00',
[SUN] = '09:00'
},
partlyClosedUntil = {
[MON] = '08:30',
[TUE] = '08:30',
[WED] = '08:30',
[THU] = '08:30',
[FRI] = '08:30',
[SAT] = '09:00',
[SUN] = '10:30'
},
closeTime = {
[MON] = '19:15',
[TUE] = '19:15',
[WED] = '19:15',
[THU] = '19:15',
[FRI] = '19:45',
[SAT] = '19:45',
[SUN] = '19:15'
},
},
{
name = 'kantoor',
enabled = true,
-- devices
idx_controller = idx_kantoor_rolluik,
idx_manualModeSwitch = idx_kantoor_rolluikManualMode,
idx_windowSensor = idx_kantoor_raam,
idx_roomTemp = idx_kantoor_thb,
idx_status = idx_kantoor_rolluikStatus,
idx_luxSensor_down = idx_omgeving_lux_achterkantBoven_avg,
idx_luxSensor_up = idx_omgeving_lux_achterkantBoven_max,
idx_sleepLate = nil,
-- settings
supportsPercentage = true,
manualOperatedDetection = true, -- manual operation detection doesn't work for all shutters
partlyOpenAtDaytime = false,
partlyOpenPercentage = 35,
openAtDaytime = false,
openAtNightTime = false,
closeAtNightTime = true,
partlyCloseWhenSunlight = false,
minSunAltitude = 10,
maxSunAltitude = 75,
minSunAzimuth = 210,
maxSunAzimuth = 350,
luxThresholdDown = 35000,
luxThresholdUp = 20000,
partlyCloseSunlightPercentage = 35,
closeWhenColdOutside = false,
closeWhenColdOutsideThreshold = 0,
closeWhenHotOutside = false,
closeWhenHotOutsideThreshold = 24,
sleepLateClosedUntil = nil,
sleepLatePartlyClosedUntil = nil,
openWhenWind = false,
maxWindSpeed = 40,
openWhenRain = false,
maxRain = 0.1,
commandDelay = 800, -- ms
closedUntil = {
[MON] = '07:00',
[TUE] = '07:00',
[WED] = '07:00',
[THU] = '07:00',
[FRI] = '07:00',
[SAT] = '08:00',
[SUN] = '09:00'
},
partlyClosedUntil = {
[MON] = '08:30',
[TUE] = '08:30',
[WED] = '08:30',
[THU] = '08:30',
[FRI] = '08:30',
[SAT] = '09:00',
[SUN] = '10:30'
},
closeTime = {
[MON] = nil,
[TUE] = nil,
[WED] = nil,
[THU] = nil,
[FRI] = nil,
[SAT] = nil,
[SUN] = nil
},
},
{
name = 'Badkamer',
enabled = true,
-- devices
idx_controller = idx_badkamer_rolluik,
idx_manualModeSwitch = idx_badkamer_rolluikManualMode,
idx_windowSensor = idx_badkamer_raam,
idx_roomTemp = idx_badkamer_thb,
idx_status = idx_badkamer_rolluikStatus,
idx_luxSensor_down = idx_omgeving_lux_voorkant_avg,
idx_luxSensor_up = idx_omgeving_lux_voorkant_max,
idx_sleepLate = nil,
-- settings
supportsPercentage = true,
manualOperatedDetection = true, -- manual operation detection doesn't work for all shutters
partlyOpenAtDaytime = true,
partlyOpenPercentage = 20,
openAtDaytime = false,
openAtNightTime = false,
closeAtNightTime = true,
partlyCloseWhenSunlight = false,
minSunAltitude = 10,
maxSunAltitude = 75,
minSunAzimuth = 30,
maxSunAzimuth = 170,
luxThresholdDown = 35000,
luxThresholdUp = 20000,
partlyCloseSunlightPercentage = 20,
closeWhenColdOutside = true,
closeWhenColdOutsideThreshold = 0,
closeWhenHotOutside = true,
closeWhenHotOutsideThreshold = 24,
sleepLateClosedUntil = nil,
sleepLatePartlyClosedUntil = nil,
openWhenWind = false,
maxWindSpeed = 40,
openWhenRain = false,
maxRain = 0.1,
commandDelay = 1000, -- ms
closedUntil = {
[MON] = '07:00',
[TUE] = '07:00',
[WED] = '07:00',
[THU] = '07:00',
[FRI] = '07:00',
[SAT] = '08:00',
[SUN] = '09:00'
},
partlyClosedUntil = {
[MON] = nil,
[TUE] = nil,
[WED] = nil,
[THU] = nil,
[FRI] = nil,
[SAT] = nil,
[SUN] = nil
},
closeTime = {
[MON] = nil,
[TUE] = nil,
[WED] = nil,
[THU] = nil,
[FRI] = nil,
[SAT] = nil,
[SUN] = nil
},
}
}
-------------------------------------------------------------
-- Configuration ends here
-- Start of script
-------------------------------------------------------------
local lastSeenCallback = 'RolluikenDevicesLastSeenCallback'
shutterControl = {
triggerDevices = function()
local tDevs = {}
for _, shutter in ipairs(SHUTTERS) do
if shutter.idx_manualModeSwitch ~= nil then tDevs[shutter.idx_manualModeSwitch] = shutter.idx_manualModeSwitch end
if shutter.idx_windowSensor ~= nil then tDevs[shutter.idx_windowSensor] = shutter.idx_windowSensor end
end
return(tDevs)
end,
allDevices = function()
local tDevs = {}
tDevs[idx_outsideTemp] = idx_outsideTemp
tDevs[idx_wind] = idx_wind
tDevs[idx_rain] = idx_rain
tDevs[idx_outsideTemp] = idx_outsideTemp
tDevs[idx_sunAltitude] = idx_sunAltitude
tDevs[idx_sunAzimuth] = idx_sunAzimuth
for _, shutter in ipairs(SHUTTERS) do
if shutter.idx_manualModeSwitch ~= nil then tDevs[shutter.idx_manualModeSwitch] = shutter.idx_manualModeSwitch end
if shutter.idx_windowSensor ~= nil then tDevs[shutter.idx_windowSensor] = shutter.idx_windowSensor end
if shutter.idx_roomTemp ~= nil then tDevs[shutter.idx_roomTemp] = shutter.idx_roomTemp end
if shutter.idx_luxSensor_down ~= nil then tDevs[shutter.idx_luxSensor_down] = shutter.idx_luxSensor_down end
if shutter.idx_luxSensor_up ~= nil then tDevs[shutter.idx_luxSensor_up] = shutter.idx_luxSensor_up end
if shutter.idx_sleepLate ~= nil then tDevs[shutter.idx_sleepLate] = shutter.idx_sleepLate end
end
return(tDevs)
end,
dumpDeviceConfig = function(dz)
local tmpName
dz.log('General devices')
if idx_outsideTemp ~= nil then tmpName = dz.devices(idx_outsideTemp).name else tmpName = 'not set' end
dz.log('.....Outside temp.: ' .. tmpName)
if idx_rain ~= nil then tmpName = dz.devices(idx_rain).name else tmpName = 'not set' end
dz.log('.....rain.........: ' .. tmpName)
if idx_wind ~= nil then tmpName = dz.devices(idx_wind).name else tmpName = 'not set' end
dz.log('.....Wind.........: ' .. tmpName)
if idx_sunAltitude ~= nil then tmpName = dz.devices(idx_sunAltitude).name else tmpName = 'not set' end
dz.log('.....Sun altitude.: ' .. tmpName)
if idx_sunAzimuth ~= nil then tmpName = dz.devices(idx_sunAzimuth).name else tmpName = 'not set' end
dz.log('.....Sun azimuth..: ' .. tmpName)
for _, shutter in ipairs(SHUTTERS) do
dz.log('Shutter name...........: ' .. shutter.name)
tmpName = dz.devices(shutter.idx_controller).name
dz.log('.....Controller........: ' .. tmpName)
if shutter.idx_manualModeSwitch ~= nil then tmpName = dz.devices(shutter.idx_manualModeSwitch).name else tmpName = 'not set' end
dz.log('.....Manual switch.....: ' .. tmpName)
if shutter.idx_windowSensor ~= nil then tmpName = dz.devices(shutter.idx_windowSensor).name else tmpName = 'not set' end
dz.log('.....Window sensor.....: ' .. tmpName)
if shutter.idx_roomTemp ~= nil then tmpName = dz.devices(shutter.idx_roomTemp).name else tmpName = 'not set' end
dz.log('.....Room temp.........: ' .. tmpName)
if shutter.idx_status ~= nil then tmpName = dz.devices(shutter.idx_status).name else tmpName = 'not set' end
dz.log('.....Status device.....: ' .. tmpName)
if shutter.idx_luxSensor_down ~= nil then tmpName = dz.devices(shutter.idx_luxSensor_down).name else tmpName = 'not set' end
dz.log('.....Lux down..........: ' .. tmpName)
if shutter.idx_luxSensor_up ~= nil then tmpName = dz.devices(shutter.idx_luxSensor_up).name else tmpName = 'not set' end
dz.log('.....Lux up............: ' .. tmpName)
if shutter.idx_sleepLate ~= nil then tmpName = dz.devices(shutter.idx_sleepLate).name else tmpName = 'not set' end
dz.log('.....Sleep late switch.: ' .. tmpName)
end
end,
isExecuting = function(dz, shutter)
local Time = require('Time')
dz.log(shutter.name .. ' lastSetpointSend = ' .. tostring(dz.data.lastSetpointSend[shutter.idx_controller]))
if dz.data.lastSetpoint[shutter.idx_controller] == nil then
dz.data.lastSetpoint[shutter.idx_controller] = dz.devices(shutter.idx_controller).level
end
if dz.data.lastSetpointSend[shutter.idx_controller] ~= nil then
local lastSetpointSend = Time(dz.data.lastSetpointSend[shutter.idx_controller])
dz.log(shutter.name .. ' lastSetpointSend is ' .. lastSetpointSend.secondsAgo .. ' seconds ago')
return lastSetpointSend.secondsAgo < COMMAND_EXECUTE_TIME
else
return false
end
end,
resetManualOperated = function(dz, shutter)
dz.data.resetManualOperated[shutter.idx_controller] = true
dz.data.manualOperated[shutter.idx_controller] = false
end,
shouldPartlyOpenForDaytime = function(dz, shutter)
local result = shutter.partlyOpenAtDaytime
local day = dz.time.wday
if dz.helpers.isHolidayToday(dz) then
day = SUN
end
local closedUntilTime = shutter.closedUntil[day]
local partlyClosedUntilTime = shutter.partlyClosedUntil[day]
if shutter.idx_sleepLate ~= nil and dz.devices(idx_sleepLate).active then
if shutter.sleepLateClosedUntil ~= nil then
closedUntilTime = shutter.sleepLateClosedUntil
end
if shutter.sleepLatePartlyClosedUntil ~= nil then
partlyClosedUntilTime = shutter.sleepLatePartlyClosedUntil
end
end
result = result and dz.variables(idxv_dagNacht).value == CONST_DAGNACHT_DAG
if closedUntilTime ~= nil then
if partlyClosedUntilTime ~= nil then
result = result and dz.time.matchesRule('between ' .. closedUntilTime .. ' and ' .. partlyClosedUntilTime)
else
result = result and dz.time.matchesRule('between ' .. closedUntilTime .. ' and 23:59')
end
end
if shutter.idx_sleepLate ~= nil and shutter.sleepLatePartlyClosedUntil == nil and dz.devices(idx_sleepLate).active and result then
dz.devices(idx_sleepLate).switchOff()
end
return result
end,
shouldOpenForDaytime = function(dz, shutter)
local result = shutter.openAtDaytime
local day = dz.time.wday
if dz.helpers.isHolidayToday(dz) then
day = SUN
end
local partlyClosedUntilTime = shutter.partlyClosedUntil[day]
if shutter.idx_sleepLate ~= nil and dz.devices(idx_sleepLate).active then
if shutter.sleepLatePartlyClosedUntil ~= nil then
partlyClosedUntilTime = shutter.sleepLatePartlyClosedUntil
end
end
result = result and dz.variables(idxv_dagNacht).value == CONST_DAGNACHT_DAG
if partlyClosedUntilTime ~= nil then
result = result and dz.time.matchesRule('between ' .. partlyClosedUntilTime .. ' and 23:59')
end
if shutter.idx_sleepLate ~= nil and dz.devices(idx_sleepLate).active and result then
dz.devices(idx_sleepLate).switchOff()
end
return result
end,
shouldCloseForNighttime = function(dz, shutter)
local result = shutter.closeAtNightTime
local day = dz.time.wday
if dz.helpers.isHolidayTomorrow(dz) then
day = SAT
end
result = result and dz.variables(idxv_dagNacht).value == CONST_DAGNACHT_NACHT
if shutter.closeTime[day] ~= nil then
result = result or dz.time.matchesRule('between ' .. shutter.closeTime[day] .. ' and 23:59')
end
return result
end,
shouldOpenForNighttime = function(dz, shutter)
local result = shutter.openAtNightTime and shutter.closeAtNightTime == false
local day = dz.time.wday
if shutter.openAtNightTime and shutter.closeAtNightTime then
dz.log(shutter.name .. ' Setting openAtNightTime and closeAtNightTime can not be both true.', dz.LOG_ERROR)
end
if dz.helpers.isHolidayTomorrow(dz) then
day = SAT
end
result = result and dz.variables(idxv_dagNacht).value == CONST_DAGNACHT_NACHT
if shutter.closeTime[day] ~= nil then
result = result or dz.time.matchesRule('between ' .. shutter.closeTime[day] .. ' and 23:59')
end
return result
end,
shouldCloseForCold = function(dz, shutter)
local result = shutter.closeWhenColdOutside
local outsideTemp = dz.devices(idx_outsideTemp)
local Time = require('Time')
local lastUpdateStr = dz.data['lastUpdate'][idx_outsideTemp]
if lastUpdateStr == nil then
dz.log(shutter.name .. ' Temperature sensor "' .. outsideTemp.name .. '"has no lastUpdate, do not close for cold', dz.LOG_ERROR)
result = false
elseif Time(lastUpdateStr).minutesAgo > TEMP_SENSOR_VALID_TIME then
if dz.data.notificationSend[outsideTemp.idx] == false then
dz.data.notificationSend[outsideTemp.idx] = true
dz.log(shutter.name .. ' Value of temperature sensor "' .. outsideTemp.name .. '"too old, lastUpdate = ' .. lastUpdateStr .. '. Do not close for cold', dz.LOG_ERROR)
else
dz.log(shutter.name .. ' Value of temperature sensor "' .. outsideTemp.name .. '"too old, lastUpdate = ' .. lastUpdateStr .. '. Do not close for cold')
end
result = false
else
dz.data.notificationSend[outsideTemp.idx] = false
dz.log(shutter.name .. ' Outside temperature = ' .. tostring(outsideTemp.temperature) .. ', Threshold value = ' .. tostring(shutter.closeWhenColdOutsideThreshold))
result = result and outsideTemp.temperature < shutter.closeWhenColdOutsideThreshold
end
return result
end,
shouldCloseForHotOutside = function(dz, shutter)
if shutter.idx_roomTemp == nil then
dz.log(shutter.name .. ' idx_roomTemp is nil')
return false
end
local result = shutter.closeWhenColdOutside
local outsideTemp = dz.devices(idx_outsideTemp)
local roomTemp = dz.devices(shutter.idx_roomTemp)
local Time = require('Time')
local lastUpdateStrOutsideTemp = dz.data['lastUpdate'][idx_outsideTemp]
local lastUpdateStrRoomTemp = dz.data['lastUpdate'][shutter.idx_roomTemp]
if lastUpdateStrOutsideTemp == nil then
dz.log(shutter.name .. ' Temperature sensor "' .. outsideTemp.name .. '"has no lastUpdate, do not close for hot outside', dz.LOG_ERROR)
result = false
elseif lastUpdateStrRoomTemp == nil then
dz.log(shutter.name .. ' Temperature sensor "' .. roomTemp.name .. '"has no lastUpdate, do not close for hot outside', dz.LOG_ERROR)
result = false
elseif Time(lastUpdateStrOutsideTemp).minutesAgo > TEMP_SENSOR_VALID_TIME then
if dz.data.notificationSend[outsideTemp.idx] == false then
dz.data.notificationSend[outsideTemp.idx] = true
dz.log(shutter.name .. ' Value of temperature sensor "' .. outsideTemp.name .. '"too old, lastUpdate = ' .. lastUpdateStrOutsideTemp .. '. Do not close for hot outside', dz.LOG_ERROR)
else
dz.log(shutter.name .. ' Value of temperature sensor "' .. outsideTemp.name .. '"too old, lastUpdate = ' .. lastUpdateStrOutsideTemp .. '. Do not close for hot outside')
end
result = false
elseif Time(lastUpdateStrRoomTemp).minutesAgo > TEMP_SENSOR_VALID_TIME then
if dz.data.notificationSend[roomTemp.idx] == false then
dz.data.notificationSend[roomTemp.idx] = true
dz.log(shutter.name .. ' Value of temperature sensor "' .. roomTemp.name .. '"too old, lastUpdate = ' .. lastUpdateStrRoomTemp .. '. Do not close for hot outside', dz.LOG_ERROR)
else
dz.log(shutter.name .. ' Value of temperature sensor "' .. roomTemp.name .. '"too old, lastUpdate = ' .. lastUpdateStrRoomTemp .. '. Do not close for hot outside')
end
result = false
else
dz.data.notificationSend[outsideTemp.idx] = false
dz.data.notificationSend[roomTemp.idx] = false
dz.log(shutter.name .. ' Outside temperature = ' .. tostring(outsideTemp.temperature) .. ', Threshold value = ' .. tostring(shutter.closeWhenColdOutsideThreshold))
result = result and outsideTemp.temperature > shutter.closeWhenHotOutsideThreshold and outsideTemp.temperature >= roomTemp.temperature
end
return result
end,
shouldCloseForSunlight = function(dz, shutter)
if idx_sunAltitude == nil or idx_sunAzimuth == nil or shutter.idx_luxSensor_up == nil or shutter.idx_luxSensor_down == nil then
dz.log(shutter.name .. ' required devices for shouldCloseForSunlight are nil')
return false
end
local result = shutter.partlyCloseWhenSunlight
local logstring = ''
if idx_sunAltitude ~= nil then
local sunAltitude = dz.devices(idx_sunAltitude)
result = result and tonumber(sunAltitude.sValue) > shutter.minSunAltitude and tonumber(sunAltitude.sValue) < shutter.maxSunAltitude
logstring = logstring .. 'sunAltitude = ' .. sunAltitude.sValue .. ' '
end
if idx_sunAzimuth ~= nil then
local sunAzimuth = dz.devices(idx_sunAzimuth)
result = result and tonumber(sunAzimuth.sValue) > shutter.minSunAzimuth and tonumber(sunAzimuth.sValue) > shutter.minSunAzimuth
logstring = logstring .. ' sunAzimuth = ' .. sunAzimuth.sValue .. ' '
end
dz.log(shutter.name .. ' ' .. logstring .. ' lux_up = ' .. tostring(dz.devices(shutter.idx_luxSensor_up).lux) .. ' lux_down = ' .. tostring(dz.devices(shutter.idx_luxSensor_down).lux))
if dz.data.closedForSun[shutter.idx_controller] then
result = result and dz.devices(shutter.idx_luxSensor_up).lux > shutter.luxThresholdUp
else
result = result and dz.devices(shutter.idx_luxSensor_down).lux > shutter.luxThresholdDown
end
dz.data.closedForSun[shutter.idx_controller] = result
return result
end,
shouldOpenForWind = function(dz, shutter)
if idx_wind == nil then
dz.log(shutter.name .. ' required devices for shouldOpenForWind are nil')
return false
end
local result = shutter.openWhenWind
local wind = dz.devices(idx_wind)
local Time = require('Time')
local lastUpdateStr = dz.data['lastUpdate'][idx_wind]
if lastUpdateStr == nil then
dz.log(shutter.name .. ' Wind sensor "' .. wind.name .. '"has no lastUpdate.', dz.LOG_ERROR)
result = false
elseif Time(lastUpdateStr).minutesAgo > WIND_SENSOR_VALID_TIME then
if dz.data.notificationSend[wind.idx] == false then
dz.data.notificationSend[wind.idx] = true
dz.log(shutter.name .. ' Value of wind sensor "' .. wind.name .. '"too old, lastUpdate = ' .. lastUpdateStr .. '.', dz.LOG_ERROR)
else
dz.log(shutter.name .. ' Value of wind sensor "' .. wind.name .. '"too old, lastUpdate = ' .. lastUpdateStr .. '.')
end
result = false
else
dz.data.notificationSend[wind.idx] = false
dz.log(shutter.name .. ' wind speed = ' .. tostring(wind.speed) .. ', Threshold value = ' .. tostring(shutter.maxWindSpeed))
result = result and wind.speed > shutter.maxWindSpeed
end
return result
end,
shouldOpenForRain = function(dz, shutter)
if idx_rain == nil then
dz.log(shutter.name .. ' required devices for shouldOpenForRain are nil')
return false
end
local result = shutter.openWhenRain
local rain = dz.devices(idx_rain)
local Time = require('Time')
local lastUpdateStr = dz.data['lastUpdate'][idx_rain]
if lastUpdateStr == nil then
dz.log(shutter.name .. ' Rain sensor "' .. rain.name .. '"has no lastUpdate.', dz.LOG_ERROR)
result = false
elseif Time(lastUpdateStr).minutesAgo > RAIN_SENSOR_VALID_TIME then
if dz.data.notificationSend[rain.idx] == false then
dz.data.notificationSend[rain.idx] = true
dz.log(shutter.name .. ' Value of rain sensor "' .. rain.name .. '"too old, lastUpdate = ' .. lastUpdateStr .. '.', dz.LOG_ERROR)
else
dz.log(shutter.name .. ' Value of rain sensor "' .. rain.name .. '"too old, lastUpdate = ' .. lastUpdateStr .. '.')
end
result = false
else
dz.data.notificationSend[rain.idx] = false
dz.log(shutter.name .. ' rain rate = ' .. tostring(rain.rainRate) .. ', Threshold value = ' .. tostring(shutter.maxRain))
result = result and rain.rainRate > shutter.maxRain
end
return result
end,
updateStatus = function(dz, idx_status, text)
if idx_status ~= nil then
local statusDev = dz.devices(idx_status)
if statusDev.text ~= text then
statusDev.updateText(text)
end
end
end,
doWorkLoop = function(dz, shutter)
dz.log(shutter.name .. ' Starting workloop')
local desiredSetpoint = 999
local isNight = dz.variables(idxv_dagNacht).value == CONST_DAGNACHT_NACHT
local isDay = dz.variables(idxv_dagNacht).value == CONST_DAGNACHT_DAG
local Time = require('Time')
local controller = dz.devices(shutter.idx_controller)
-- Check first if any action is allowed
if shutter.enabled == false then
dz.log(shutter.name .. ' Shutter configuration is disabled, do nothing')
shutterControl.updateStatus(dz, shutter.idx_status, 'Shutter configuration is disabled.')
return
end
if shutter.idx_manualModeSwitch ~= nil and dz.devices(shutter.idx_manualModeSwitch).active then
dz.log(shutter.name .. ' Manual mode switch is active, do nothing')
shutterControl.updateStatus(dz, shutter.idx_status, 'Manual mode switch is active.')
return
end
if dz.data.manualOperated[shutter.idx_controller] then
dz.log(shutter.name .. ' Shutter is manual operated, do nothing')
return
end
if shutterControl.isExecuting(dz, shutter) then
dz.log(shutter.name .. ' Shutter is executing previous command, do nothing')
return
end
if dz.data.resetManualOperated[shutter.idx_controller] == nil or dz.data.resetManualOperated[shutter.idx_controller] then
dz.log(shutter.name .. ' Reset manual mode.')
dz.data.resetManualOperated[shutter.idx_controller] = false
dz.data.lastSetpoint[controller.idx] = controller.level
elseif shutter.supportsPercentage and shutter.manualOperatedDetection and controller.level ~= dz.data.lastSetpoint[shutter.idx_controller] then
dz.log(shutter.name .. ' Manual mode detected. Controller level = ' .. controller.level .. ' and last setpoint = ' .. tostring(dz.data.lastSetpoint[shutter.idx_controller]))
shutterControl.updateStatus(dz, shutter.idx_status, 'Shutter is manual operated.')
dz.data.manualOperated[shutter.idx_controller] = true
return
elseif shutter.supportsPercentage == false and ((dz.data.lastSetpoint[shutter.idx_controller] >= 50 and dz.data.lastSetpoint[shutter.idx_controller] <= 100 and controller.state ~= 'Open') or (dz.data.lastSetpoint[shutter.idx_controller] < 50 and controller.state ~= 'Closed')) then
dz.log(shutter.name .. ' Manual mode detected. Controller state = ' .. controller.state .. ' and last setpoint = ' .. tostring(dz.data.lastSetpoint[shutter.idx_controller]))
shutterControl.updateStatus(dz, shutter.idx_status, 'Shutter is manual operated.')
dz.data.manualOperated[shutter.idx_controller] = true
return
end
if shutter.idx_windowSensor ~= nil then
local lastUpdateStr = dz.data['lastUpdate'][shutter.idx_windowSensor]
local windowSensor = dz.devices(shutter.idx_windowSensor)
if windowSensor.active then
dz.log(shutter.name .. ' Window is open, do nothing')
shutterControl.updateStatus(dz, shutter.idx_status, 'Window is open.')
return
elseif lastUpdateStr == nil then
dz.log(shutter.name .. ' Windows sensor "' .. windowSensor.name .. '"has no lastUpdate, abort work loop for shutter ' .. shutter.name, dz.LOG_ERROR)
shutterControl.updateStatus(dz, shutter.idx_status, 'Windows sensor "' .. windowSensor.name .. '"has no lastUpdate')
return
elseif Time(lastUpdateStr).minutesAgo > WINDOW_SENSOR_VALID_TIME then
if dz.data.notificationSend[windowSensor.idx] == false then
dz.data.notificationSend[windowSensor.idx] = true
dz.log('Value of windows sensor "' .. windowSensor.name .. '"too old, lastUpdate = ' .. lastUpdateStr .. '. Abort work loop for shutter ' .. shutter.name, dz.LOG_ERROR)
else
dz.log('Value of windows sensor "' .. windowSensor.name .. '"too old, lastUpdate = ' .. lastUpdateStr .. '. Abort work loop for shutter ' .. shutter.name)
end
shutterControl.updateStatus(dz, shutter.idx_status, 'Value of windows sensor "' .. windowSensor.name .. '"too old, lastUpdate = ' .. lastUpdateStr .. '.')
return
end
dz.data.notificationSend[windowSensor.idx] = false
end
-- determine desired setpoint
-- lowest setpoint should be send to shutter
local newStatus
if shutterControl.shouldCloseForNighttime(dz, shutter) then
dz.log(shutter.name .. ' shouldCloseForNighttime = true')
newStatus = 'Closed for night time.'
desiredSetpoint = 0
end
if 0 < desiredSetpoint and shutterControl.shouldCloseForCold(dz, shutter) then
dz.log(shutter.name .. ' shouldCloseForCold = true')
newStatus = 'Closed for cold'
desiredSetpoint = 0
end
if 0 < desiredSetpoint and shutterControl.shouldCloseForHotOutside(dz, shutter) then
dz.log(shutter.name .. ' shouldCloseForHotOutside = true')
newStatus = 'Closed because it is hot outside.'
desiredSetpoint = 0
end
if shutter.partlyOpenPercentage < desiredSetpoint and shutterControl.shouldPartlyOpenForDaytime(dz, shutter) then
dz.log(shutter.name .. ' shouldPartlyOpenForDaytime = true')
newStatus = 'Partly open for day time.'
desiredSetpoint = math.min(desiredSetpoint, shutter.partlyOpenPercentage)
end
if shutter.partlyCloseSunlightPercentage < desiredSetpoint and shutterControl.shouldCloseForSunlight(dz, shutter) then
dz.log(shutter.name .. ' shouldCloseForSunlight = true')
newStatus = 'Partly closed for sunlight.'
desiredSetpoint = math.min(desiredSetpoint, shutter.partlyCloseSunlightPercentage)
end
if 100 < desiredSetpoint and shutterControl.shouldOpenForDaytime(dz, shutter) then
dz.log(shutter.name .. ' shouldOpenForDaytime = true')
newStatus = 'Open for daytime.'
desiredSetpoint = math.min(desiredSetpoint, 100)
end
if 100 < desiredSetpoint and shutterControl.shouldOpenForNighttime(dz, shutter) then
dz.log(shutter.name .. ' shouldOpenForNighttime = true')
newStatus = 'Open for night time.'
desiredSetpoint = math.min(desiredSetpoint, 100)
end
if desiredSetpoint ~= 100 and shutterControl.shouldOpenForWind(dz, shutter) then
dz.log(shutter.name .. ' shouldOpenForWind = true')
newStatus = 'Open for wind.'
desiredSetpoint = 100
end
if desiredSetpoint ~= 100 and shutterControl.shouldOpenForRain(dz, shutter) then
dz.log(shutter.name .. ' shouldOpenForRain = true')
newStatus = 'Open for rain.'
desiredSetpoint = 100
end
dz.log(shutter.name .. ' desiredSetpoint = ' .. desiredSetpoint)
dz.log(shutter.name .. ' controller level = ' .. controller.level .. ' ' .. controller.state)
-- Send setpoint to shutter
if desiredSetpoint <= 100 then
shutterControl.updateStatus(dz, shutter.idx_status, newStatus)
if shutter.supportsPercentage and (desiredSetpoint ~= controller.level or desiredSetpoint ~= dz.data.lastSetpoint[controller.idx] or (desiredSetpoint == 0 and controller.state == 'Open') or (desiredSetpoint == 100 and controller.state == 'Closed')) then
-- if desiredSetpoint == 0 then
-- controller.close()
-- elseif desiredSetpoint == 100 then
-- controller.open()
-- else
controller.dimTo(desiredSetpoint).afterSec(shutter.commandDelay/1000)
-- end
dz.data.lastSetpointSend[controller.idx] = dz.time.rawDateTime
dz.data.lastSetpoint[controller.idx] = desiredSetpoint
dz.log(shutter.name .. ' new setpoint send to controller (' .. controller.idx .. '): ' .. dz.data.lastSetpoint[controller.idx] .. ' at ' .. dz.data.lastSetpointSend[controller.idx])
elseif shutter.supportsPercentage == false and ((desiredSetpoint >= 50 and controller.state ~= 'Open') or (desiredSetpoint < 50 and controller.state ~= 'Closed') or desiredSetpoint ~= dz.data.lastSetpoint[controller.idx]) then
if desiredSetpoint >= 50 then
controller.open()
else
controller.close()
end
dz.data.lastSetpointSend[controller.idx] = dz.time.rawDateTime
dz.data.lastSetpoint[controller.idx] = desiredSetpoint
dz.log(shutter.name .. ' new setpoint send to controller: ' .. dz.data.lastSetpoint[controller.idx] .. ' at ' .. dz.data.lastSetpointSend[controller.idx])
end
end
end,
}
return {
on = {
devices = shutterControl.triggerDevices(),
timer = { 'every minute' },
variables = { idxv_dagNacht },
httpResponses = { lastSeenCallback }
},
data = {
manualOperated = {initial = {}}, --true/false per controller, nil should be handled as true
resetManualOperated = {initial = {}}, --true/false per controller,
lastUpdate = {initial = {}}, --last update rawDateTime
lastSetpointSend = {initial = {}}, --last setpoint send to shutter
lastSetpoint = {initial = {}}, --rawDateTime of last setpoint command
notificationSend = {initial = {}}, -- true/false notification already send, so don't send every minute
closedForSun = {initial = {}} -- true/false per controller
},
logging = {
-- level = domoticz.LOG_DEBUG,
-- level = domoticz.LOG_INFO,
level = domoticz.LOG_ERROR,
marker = "Rolluiken"
},
execute = function(dz, triggeredItem, info)
local doWorkloopForAll = false
if dumpDeviceConfigToLog then shutterControl.dumpDeviceConfig(dz) end
if triggeredItem.isDevice then
-- find the device which caused the trigger
for _, shutter in ipairs(SHUTTERS) do
if triggeredItem.idx == shutter.idx_manualModeSwitch and triggeredItem.active == false then
-- manual mode switched off
dz.log('Manual mode switch is turned off for ' .. shutter.name)
shutterControl.resetManualOperated(dz, shutter)
-- do workloop for only this shutter
shutterControl.doWorkLoop(dz, shutter)
elseif triggeredItem.idx == shutter.idx_windowSensor and triggeredItem.active == false then
-- window is closed
dz.log('Window is closed for ' .. shutter.name)
shutterControl.doWorkLoop(dz, shutter)
end
end
elseif triggeredItem.isVariable and triggeredItem.id == idxv_dagNacht then
for _, shutter in ipairs(SHUTTERS) do
shutterControl.resetManualOperated(dz, shutter)
shutterControl.doWorkLoop(dz, shutter)
end
elseif triggeredItem.isTimer then
-- request last update values
dz.openURL({url = dz.settings['Domoticz url'] .. '/json.htm?type=devices&used=true',callback = lastSeenCallback })
for _, shutter in ipairs(SHUTTERS) do
-- triggered by timer do work
shutterControl.doWorkLoop(dz, shutter)
end
elseif triggeredItem.isHTTPResponse then
-- process http response of last update request
local devs = shutterControl.allDevices()
for _, node in pairs(triggeredItem.json.result) do
local idx = tonumber(node.idx)
if devs[idx] then
dz.log(node.Name .. ' was last updated at ' .. node.LastUpdate, dz.LOG_DEBUG)
dz.data['lastUpdate'][idx] = node.LastUpdate
end
end
end
end
}