Timer between but not crossing dates

Easy to use, 100% Lua-based event scripting framework.

Moderator: leecollings

Post Reply
sciurius
Posts: 55
Joined: Monday 26 July 2021 16:39
Target OS: Raspberry Pi / ODroid
Domoticz version: 2021.1
Location: NL
Contact:

Timer between but not crossing dates

Post by sciurius »

I want to switch on a light from a given time, say 07:30, until sunrise. From the docs I understand that

Code: Select all

on = {
    timer = {
        'between 07:30 and sunrise'
    }
}
will work as long as sunrise falls after 07:30 but cross the date if it falls before, i.e. the light is switched off at sunrise and switched on at 07:30 so it stays on until the next sunrise.

What is the correct (and easy :)) way to handle this?
User avatar
user4563
Posts: 114
Joined: Tuesday 19 October 2021 17:24
Target OS: Linux
Domoticz version: 2025.2
Contact:

Re: Timer between but not crossing dates

Post by user4563 »

The easy way would be to use the timer in the desired switch in the switches tab. Turn on at 7:30 and turn off at sunrise.
ODroid H4+ / Ubuntu server 24.04 LTS
Aeotec Z-Stick Gen5+
ZWave-JS-UI / Mosquitto MQTT
Domoticz for Android
Domoticz Scenes add-on in Kodi
sciurius
Posts: 55
Joined: Monday 26 July 2021 16:39
Target OS: Raspberry Pi / ODroid
Domoticz version: 2021.1
Location: NL
Contact:

Re: Timer between but not crossing dates

Post by sciurius »

If sunrise falls before 07:30 that would keep the light burning all day except between sunrise and 07:30...

I think I need something similar to

Code: Select all

local lamp = 'lamp1'

return {
    on = {
       	timer = {
	    '07:30',
	    'at sunrise'
	}
    },
    execute = function( domoticz, device, triggerInfo )
	local Time = require('Time')
	local currenttime = Time();

	if ( triggerInfo.trigger == '07:30' ) then
	    if ( currentTime < Time('sunrise') ) then
		domoticz.devices(lamp).switchOn()
	    end
	else
	    domoticz.devices(lamp).switchOff()
	end
    end
}
User avatar
user4563
Posts: 114
Joined: Tuesday 19 October 2021 17:24
Target OS: Linux
Domoticz version: 2025.2
Contact:

Re: Timer between but not crossing dates

Post by user4563 »

No not in dzVents, in the built-in timers. Go to: Switches (Tab) > Timers (lamp)
It will automatically handle the midnight crossing.
You'll be setting 2 commands - one off, one on.
ODroid H4+ / Ubuntu server 24.04 LTS
Aeotec Z-Stick Gen5+
ZWave-JS-UI / Mosquitto MQTT
Domoticz for Android
Domoticz Scenes add-on in Kodi
User avatar
boum
Posts: 136
Joined: Friday 18 January 2019 11:31
Target OS: Raspberry Pi / ODroid
Domoticz version: 4.10717
Location: France
Contact:

Re: Timer between but not crossing dates

Post by boum »

The problem with built-in timers is when sunrise will be earlier than 7:30, at this point, the lamp will switch off at sunrise and later turn on at 7:30.

Same script as above but simpler:

Code: Select all

local lamp = 'lamp1'

return {
    on = {
       	timer = {
	    '07:30',
	    'at sunrise'
	}
    },
    execute = function(domoticz, item)
	if (item.trigger == 'at sunrise') then
	    domoticz.devices(lamp).switchOff()
	    return -- early exit
	end
	if domoticz.time.isNightTime then
	    domoticz.devices(lamp).switchOn()
	end
    end
}
sciurius
Posts: 55
Joined: Monday 26 July 2021 16:39
Target OS: Raspberry Pi / ODroid
Domoticz version: 2021.1
Location: NL
Contact:

Re: Timer between but not crossing dates

Post by sciurius »

Sounds like a good suggestion. Thanks.
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: Timer between but not crossing dates

Post by rrozema »

The easiest way is to not use timers in the script, but make the script react to dummy devices: create 2 dummy switches. For example:
  • "Sun up" - to indicate when you want the light to be off because the sun is up.
  • "Outside lights" - to indicate the time frame in which you want the lights to go on.
Now you go through the UI and click the 'Timers' button on the "Sun up" device and set it to go On at sunrise and Off at sunset. For the "Outside lights" you do the same and set it to go On at "exact time" 7:00 and Off at like "exact time" 23:00 or so.

Now create the script like this:

Code: Select all

-- Specify the name of the 1st dummy switch here.
local SUNUP_NAME = 'Sun up'

-- Specify the name of the 2nd dummy switch here.
local LIGHTSON_NAME = 'Outside lights'

-- Specify the names of the switches for your garden or porch lights
-- here. If you want to switch multiple lights specify them like
-- { 'light1', 'light2', 'light3' }
local LIGHTS = { 'lamp1' }

return {
	on = {
		devices = {
			SUNUP_NAME,
			LIGHTSON_NAME
		}
	},
	execute = function(domoticz, device)
	    domoticz.log('Device ' .. device.name .. ' was changed', domoticz.LOG_INFO)
	    
	    if device.isDevice and (SUNUP_NAME == device.name or LIGHTSON_NAME == device.name) then
            local sunup = domoticz.devices(SUNUP_NAME)
            local lightson = domoticz.devices(LIGHTSON_NAME)

    	    if nil == sunup then
    	        domoticz.log( "Missing device " .. SUNUP_NAME .. ".", domoticz.LOG_ERROR)
    	    elseif nil == lightson then
    	        domoticz.log( "Missing device " .. LIGHTSON_NAME .. ".", domoticz.LOG_ERROR)
    	    else
    	        domoticz.devices().filter(LIGHTS).forEach(
    	                function(light)
            		    if not sunup.active and lightson.active then
            		        if not light.active then
            		            light.switchOn()
            		        end
                            else
                                if light.active then
                	            light.switchOff()
                	        end
                            end
    	                end
	            )
	        end
	    end
	end
}
Once you've got the script installed you can test if it works by clicking the both dummy switches: if the "Sun up" switch is On, the light should remain off whether or not you have the "Outside lights" On or Off. But, if the "Sun up" button is off, the light Should go On and Off with that "Outside lights" button. Now, since you've already installed the timers on those both buttons to automatically switch at the right times, the light will go On at 7:00, but only if the sun is not up at that time, and it will go Off when the sun rises, then when the sun sets before 23:00, the lights will go on, and at 23:00 they will go off again.
And the best thing is: if one day you want the light to be On after 23:00 (f.e. because you're expecting a late visitor), you just click the "Outside lights" button at 23:01 (after the lights have gone Off) and the lights will go On again, and they'll remain On until either you switch the "Outside lights" button Off or until the next morning the sun rises: the "programming" will just pick up where you left it at the next time a switch changes.
One more hint: exactly at sunrise and sunset is most likely not the right time to switch the "Sun up" switch. I have mine set to go On at 45 minutes after sunrise and Off at 45 minutes before sunset. And the nice thing is: you don't need to re-edit the script to experiment with the time. You can change it by changing the timer on the "Sun up" switch in the UI.
Last edited by rrozema on Tuesday 23 November 2021 15:28, edited 1 time in total.
EddyG
Posts: 1042
Joined: Monday 02 November 2015 5:54
Target OS: -
Domoticz version:

Re: Timer between but not crossing dates

Post by EddyG »

You could also use 'sunriseInMinutes' and see if that more or less than 450 ( 7 x 60 + 30 == 07:30) and change the script accordingly.
sciurius
Posts: 55
Joined: Monday 26 July 2021 16:39
Target OS: Raspberry Pi / ODroid
Domoticz version: 2021.1
Location: NL
Contact:

Re: Timer between but not crossing dates

Post by sciurius »

Yes, that's another option.

rrozema's script has the advantages mentioned in the posting and I extended it to use two 'sun' periods: sunrise/sunset and sunrise-0:15/sunset+0:15. I use the first when it is raining. It controls multiple lamps, and the next challenge is to add individual randomization to the lamps.
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: Timer between but not crossing dates

Post by rrozema »

sciurius wrote: Wednesday 24 November 2021 10:46 Yes, that's another option.

rrozema's script has the advantages mentioned in the posting and I extended it to use two 'sun' periods: sunrise/sunset and sunrise-0:15/sunset+0:15. I use the first when it is raining. It controls multiple lamps, and the next challenge is to add individual randomization to the lamps.
What do you mean by "individual randomization"? That one light can switch of/on x minutes before the other, where x is a random number from n to m?

That effect can be achieved by adding the commandOption .withinMin() to the switchOn() and switchOff commands.

Code: Select all

light.switchOn().withinMin(10)
sciurius
Posts: 55
Joined: Monday 26 July 2021 16:39
Target OS: Raspberry Pi / ODroid
Domoticz version: 2021.1
Location: NL
Contact:

Re: Timer between but not crossing dates

Post by sciurius »

Exactly. Works like a charm.
EddyG
Posts: 1042
Joined: Monday 02 November 2015 5:54
Target OS: -
Domoticz version:

Re: Timer between but not crossing dates

Post by EddyG »

@rrozema Just curious.
Only 2 devices are used as trigger devices. Why do you check for device.isDevice and why do you check "if nil == sunup then"
Are not those checks redundant? B.t.w. my line would have been "if sunup == nil then"
And why "if not light.active then" could that not be changed in just 1 line "light.switchOn().checkFirst()"? Or leave the checkFirst all together?
Also the function "domoticz.devices().filter(LIGHTS).forEach(" takes a lot more time and memory than just a walk thru a few devices.
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: Timer between but not crossing dates

Post by rrozema »

EddyG wrote: Thursday 25 November 2021 10:09 @rrozema Just curious.
Only 2 devices are used as trigger devices. Why do you check for device.isDevice and why do you check "if nil == sunup then"
Are not those checks redundant? B.t.w. my line would have been "if sunup == nil then"
And why "if not light.active then" could that not be changed in just 1 line "light.switchOn().checkFirst()"? Or leave the checkFirst all together?
Also the function "domoticz.devices().filter(LIGHTS).forEach(" takes a lot more time and memory than just a walk thru a few devices.
This is one script I use myself, only some names have been changed to fit the poster's question and to make it more clear as an example. I like my coding robust and thus I always try to put in the 'safety checks' on the type of the device and the creation of the devices. This saves myself a lot of time in debugging if something is wrong later, when I try to make changes in the code or as in this case, when I give the code to someone else and they try to run the script but forgets some preparation step or make a change/ an addition to script for their own needs.

The device variable is another one than the sunup, so their values may be different (and will be, when the other switch got engaged). device is the parameter passed to the script by domoticz/dzVents, the sunup value is that retrieved from the devices collection given the name that was specified in the top: there may be a typo in the name specified for SUNUP_NAME, the device may have been renamed, or there may be a bug somewhere in domoticz or dzvents. Multiple reasons can be thought of why the names could not match. I like my coding to be robust and if something is not as expected, I want to be informed so that I can check to see if my expectations are wrong or that something else is amiss. I am only human and as such I make mistakes -a lot-, and so are and do the developers of Domoticz & dzVents. We can't blame any of them nor me for that. But we can prepare for it by being explicit in our coding.

The if nil == sunup then is actually a programming habit intended for coding safety too. An often made error is to type a single = instead of a ==, which is an assignment instead of a comparison. I come from a C background (long time ago), and in C "x = nul" is a valid boolean expression that first assigns x the nul value, then evaluates to false. i.e. "if x = nul" is syntactically correct and accepted by the compiler, but most likely not what you intended. This error can be very hard to locate in a large chunk of code. If however you are in the habit of always putting the constant value on the left hand side of every comparison, f.e. nul = x, that does result in a compile error: a constant can not be assigned a value. And thereby the error of writing a single = where a double == was intended is very easily located. LUA has all sorts of automatic type conversions and language constructs, including some involving assignments being treated as expressions. I'm 100% convinced that I do not know and never will know all of the details of the LUA language, so I apply this same habit to this language to, to save myself a lot of time if I accidently type = instead of ==.

The domoticz.devices().filter(LIGHTS).forEach( construct is there because I want uniformity, again for robustness reasons. If my code works for one light, it should work the same for multiple lights. I treat the devices().filter(<list>) as a black box. Not looking at it's implementation, nor making any assumptions on it's internals. By doing it this way, if we find that filter(<list>) is to slow, we can choose to optimise it and thereby fix the issue in each and every call to that function. If however I would have coded it to explicity to walk the list, then retrieve the device and call that device's switchOn() method, I owuld have to go through each and every script ever made to get the same optimisation. In fact, I did look at the implementation of devices().filter(<list>) some time ago because I had the same reservations you have, and saw that you are right: it simply walks the entire list. Yet, performance of this construct has never been a problem for me, so an optimisation was not really needed. There is another programming mantra for that: first make sure it works correctly, then optimise it. In this case a huge optimisation could be achieved by keeping the devices in alphabetical order and doing a binary search in filter() instead of looping through all devices as it is implemented now. I'm convinced this would in fact help performance in a lot more places. But, if nobody -including me- notices a relevant performance degradation, why bother optimising it?

Finally, your question on checkFirst() is a very good one, because I'm a bit contradicting my previous point for using filter() here. There is however a reason for that: I've seen checkFirst() fail many times, in my own code and in the forum posts. For multiple reasons, often simple ones like the device type wasn't supported, as is perfectly documented, but he, I'm human and I forget... So that is why I avoid using checkFirst() and put an explicit if in my code to avoid calling switchOn or switchOf or setLevel (one that checkFirst doesn't support), etc if the device already has the value I want it to have.
EddyG
Posts: 1042
Joined: Monday 02 November 2015 5:54
Target OS: -
Domoticz version:

Re: Timer between but not crossing dates

Post by EddyG »

You are right. I am used to optimize my scripts for speed. I had several instances in the past where Domoticz hits the limit of 10 sec. timeout for dzVents. That is also the reason I asked for logging of the scriptname in case the timeout was reached. I had to re-write a lot of the scripts in the async methode, that's why I go for speed.
rrozema
Posts: 470
Joined: Thursday 26 October 2017 13:37
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Delft
Contact:

Re: Timer between but not crossing dates

Post by rrozema »

How many devices do you have in Domoticz and how many dzVents scripts do you have enabled in dzVents? Just curious, as I have 797 devices with a total of 16 enabled scripts and I never have that 10 seconds warning. I have hardly no timer-triggered scripts, so my script calls will mostly be from device triggers only and therefor they will most of the times not fire all at the same time like with timer-triggered scripts. Maybe that's the difference?
EddyG
Posts: 1042
Joined: Monday 02 November 2015 5:54
Target OS: -
Domoticz version:

Re: Timer between but not crossing dates

Post by EddyG »

I only have 355 devices but 70+ scripts of which 80% is timer based and a lot of them 'every minute' at least in a timespan.
Lights go on/off and roller shutters up/down based on Lux, time, presence, holiday, etc....
I even use your Auto-On/Off script with a lot of presence PIR devices (the idea modified on a devices base) and also bases on Lux and time.
sciurius
Posts: 55
Joined: Monday 26 July 2021 16:39
Target OS: Raspberry Pi / ODroid
Domoticz version: 2021.1
Location: NL
Contact:

Re: Timer between but not crossing dates

Post by sciurius »

rrozema wrote: Thursday 25 November 2021 13:23There is another programming mantra for that: first make sure it works correctly, then optimise it
The first rule of optimization is: Don’t do it. The second rule of optimization (for experts only) is: Don’t do it yet.
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest