Hi there, first ‘real’ question on this forum from my side, so here goes. I’ve written a script in Domoticz which controls my outdoor lights, based on time, lux and motion sensors. This script is written in LUA with the ‘dzVents’ engine. I would like to get some advice on how to best do this with Node-Red.
For your information on my ‘experience level’; i am currently employed as a software architect with over 20+ years of experience and probably touched as much programming languages in the same time; so i am not afraid to put my entire LUA script in a ‘function’ block but still i think there are some bumps to take.
First i’ll paste the script i’ve written (for others to take inspiration from) and then give you a rundown per segment what it does. After each segment i will post the questions i have regarding how to do this in node-red.
return {
active = true,
on = {
['devices'] = {
'Voordeur - Beweging',
'Tuin - Beweging BBQ'
},
['timer'] = {
'every minute'
}
},
data = {
insideStatus = { initial = nil },
outsideStatus = { initial = nil }
},
execute = function(domoticz, item)
-- -------------------------------------------------------------------------------
-- Dimming variables
-- -------------------------------------------------------------------------------
local loweredPercentage = 10 -- When we sleep and the lights are on, the dimming level will be ...
local normalPercentage = 20 -- When we are awake and the lights are on, the dimming level will be ...
local highPercentage = 100 -- When movement is detected during the time frame that the lights should be on, the lights are brightend to ...
local brightDurationMinMinutes = 3 -- For this amount of minutes ...
local brightDurationMaxMinutes = 4 -- And if (after this amount of minutes) the lights are still bright, we dim them (safeguard for missing timer events)
-- -------------------------------------------------------------------------------
-- Supporting functions
-- -------------------------------------------------------------------------------
local log = function(msg)
domoticz.log(msg, domoticz.LOG_INFO)
end
local setDeviceToLevelAfterSecs = function(device, dimLevel, secondsBeforeWeChange)
-- Only continue if the device does not yet have this state
local stateToCheck = (dimLevel > 0)
-- Check state & make sure at least 'x' seconds have passed sinds the last update
if ((domoticz.devices(device).bState ~= stateToCheck) and (domoticz.devices(device).lastUpdate.secondsAgo >= secondsBeforeWeChange)) then
if (stateToCheck and (domoticz.devices(device).level ~= dimLevel)) then
-- If we should be on and the level != the required level, set it to the right level
domoticz.devices(device).dimTo(dimLevel)
else
-- Else we should be off
domoticz.devices(device).switchOff()
end
end
end
-- -------------------------------------------------------------------------------
-- Calculate level to which the lights should dim
-- -------------------------------------------------------------------------------
-- This holds the current hour and minute (format HHMM)
local currentHourAndMinutes = tonumber(os.date("%H%M"))
-- By default, we dim to the 'normal' percentage
local dimLevel = normalPercentage
-- Between these times, we have a 'minimum' dimming level
if ((currentHourAndMinutes >= 2130) or (currentHourAndMinutes < 0630)) then
dimLevel = loweredPercentage
end
-- -------------------------------------------------------------------------------
-- In case script is triggered via a Timer
-- -------------------------------------------------------------------------------
if (item.isTimer) then
log('Script activated by a timer event')
local weightedLux = tonumber(domoticz.devices('Lux - Voordeur').lux);
-- -------------------------------------------------------------------------------
-- Determine current 'inside status'
-- -------------------------------------------------------------------------------
if (currentHourAndMinutes < 1200) then
if ((weightedLux > 15) and (domoticz.data.insideStatus ~= 'off')) then
domoticz.data.insideStatus = 'off'
-- Turn off lights
domoticz.devices('Woonkamer - Spotjes').switchOff()
end
end
if (currentHourAndMinutes > 1200) then
if ((weightedLux < 15) and (domoticz.data.insideStatus ~= 'evening')) then
domoticz.data.insideStatus = 'evening'
-- Turn on lights
domoticz.devices('Woonkamer - Spotjes').dimTo(15)
end
end
-- -------------------------------------------------------------------------------
-- Determine current 'outside status'
-- -------------------------------------------------------------------------------
if (domoticz.time.matchesRule('at 06:00-23:00')) then
if ((weightedLux < 5) and (domoticz.data.outsideStatus ~= 'evening')) then
domoticz.data.outsideStatus = 'evening'
end
if ((weightedLux > 5) and (domoticz.data.outsideStatus ~= 'off')) then
domoticz.data.outsideStatus = 'off'
end
else
if (domoticz.data.outsideStatus ~= 'midnight') then
domoticz.data.outsideStatus = 'midnight'
end
end
-- -------------------------------------------------------------------------------
-- Switch based on 'outside status'
-- -------------------------------------------------------------------------------
local brightDurationMaxSeconds = (brightDurationMaxMinutes * 60)
if (domoticz.data.outsideStatus == 'evening') then
setDeviceToLevelAfterSecs('Voordeur - Spotjes', dimLevel, brightDurationMaxSeconds)
setDeviceToLevelAfterSecs('Tuin - Spotjes gevel', dimLevel, brightDurationMaxSeconds)
setDeviceToLevelAfterSecs('Tuin - Spotjes tuinhuis', dimLevel, brightDurationMaxSeconds)
end
if (domoticz.data.outsideStatus == 'midnight') then
setDeviceToLevelAfterSecs('Tuin - Spotjes gevel', 0, brightDurationMaxSeconds)
setDeviceToLevelAfterSecs('Tuin - Spotjes tuinhuis', 0, brightDurationMaxSeconds)
end
if (domoticz.data.outsideStatus == 'off') then
setDeviceToLevelAfterSecs('Voordeur - Spotjes', 0, 0)
setDeviceToLevelAfterSecs('Tuin - Spotjes gevel', 0, 0)
setDeviceToLevelAfterSecs('Tuin - Spotjes tuinhuis', 0, 0)
end
end
-- -------------------------------------------------------------------------------
-- Device event
-- -------------------------------------------------------------------------------
if (item.isDevice) then
log('Script activated by a device event: ' .. item.name)
-- -------------------------------------------------------------------------------
-- Movement detected at front door
-- -------------------------------------------------------------------------------
if (item.name == 'Voordeur - Beweging') then
if (domoticz.devices('Voordeur - Beweging').bState) then
if (domoticz.data.outsideStatus ~= 'off') then
domoticz.devices('Voordeur - Spotjes').dimTo(highPercentage)
domoticz.devices('Voordeur - Spotjes').dimTo(dimLevel).afterMin(brightDurationMinMinutes)
end
end
end
-- -------------------------------------------------------------------------------
-- Movement detected in the garden
-- -------------------------------------------------------------------------------
if (item.name == 'Tuin - Beweging BBQ') then
if (domoticz.devices('Tuin - Beweging BBQ').bState) then
if (domoticz.data.outsideStatus == 'evening') then
domoticz.devices('Tuin - Spotjes gevel').dimTo(highPercentage)
domoticz.devices('Tuin - Spotjes tuinhuis').dimTo(highPercentage)
domoticz.devices('Tuin - Spotjes gevel').dimTo(dimLevel).afterMin(brightDurationMinMinutes)
domoticz.devices('Tuin - Spotjes tuinhuis').dimTo(dimLevel).afterMin(brightDurationMinMinutes)
end
if (domoticz.data.outsideStatus == 'midnight') then
domoticz.devices('Tuin - Spotjes gevel').dimTo(highPercentage)
domoticz.devices('Tuin - Spotjes tuinhuis').dimTo(highPercentage)
domoticz.devices('Tuin - Spotjes gevel').switchOff().afterMin(brightDurationMinMinutes)
domoticz.devices('Tuin - Spotjes tuinhuis').switchOff().afterMin(brightDurationMinMinutes)
end
end
end
end
end
}
The above script is off course monolithic and huge and probably can be done in a nicer way. Here is the rundown:
on = {
['devices'] = {
'Voordeur - Beweging',
'Tuin - Beweging BBQ'
},
['timer'] = {
'every minute'
}
},
The script is triggered every minute
on a timer, AND if either one of the devices mentioned in the devices list send an update to OpenZwave (in use by Domoticz). My ‘front door’ motion sensor (the one without BBQ in it ) is used throughout the script for the LUX values.
The devices in question are:
Tuin - Beweging BBQ
Device type: Fibaro Motion Sensor (Z-Wave Plus)
Powered via batteryVoordeur - Beweging
Device type: Aeotec 6-in-1 Multisensor GEN5
Powered via USB power
The current status is that i have not yet installed HASS.io; but i do have a dockerized version of node-red running. I am in preference of having node-red separated from the hass.io install. Reason being is that i would first want to convert the LUA script from dzVents to Node-red (still using Domoticz, but via MQTT) and then install HASS.io (in order to limit my downtime) and use Node-red-contrib-home-assistant-websocket to implement my script.
Question 1: for the timer part i think i should use a ‘timestamp’ block with interval 1 minute?
Question 2: for the devices would need to be the home assistant device triggers?
data = {
insideStatus = { initial = nil },
outsideStatus = { initial = nil }
}
These are script variables which hold the current status (e.g. state) that the lights should be on. This is persisted on disk and therefore would ‘survive’ a reboot of Domoticz. I assume i could use persistency for this
Question 3: i think i need to know if and how i can access the same functionality from Node-red, that i use in the script:
domoticz.devices(device)
This gets the current state of the device; i am interested in the following properties (they might be named differently in HA off course):
domoticz.devices(device).bState
Boolean indicating the on/off state
domoticz.devices(device).level
Integer showing the current dimming level
domoticz.devices(device).lux
Gets a LUX value of a motion sensor device
domoticz.devices(device).lastUpdate.secondsAgo
Integer showing the number of seconds the device was last updated. I use this for instance to prevent multiple switch commands being sent amongst other things.
domoticz.devices(device).dimTo(dimLevel)
Turns on a device if off, and dims it to a certain level
domoticz.devices(device).switchOff()
Turns a device off
If i can access this all from a function block (using a home-assistant object?) i can write my script entirely in that one function block (with inputs the state change events and the timer block)