Run script if device left on for a period of time?  [Solved]

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

Moderator: leecollings

Post Reply
doh
Posts: 82
Joined: Monday 01 December 2014 13:28
Target OS: Raspberry Pi / ODroid
Domoticz version: Beta
Location: London, UK
Contact:

Run script if device left on for a period of time?

Post by doh »

I know it's possible to detect if a device is turned on, and also to determine how long it has been left on for.
However, is it possible to trigger a script based on whether a device has been left on for, say, an hour, rather than having a script running every few minutes to check this?
Thanks
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Run script if device left on for a period of time?

Post by waaren »

doh wrote: Friday 03 May 2019 12:29 I know it's possible to detect if a device is turned on, and also to determine how long it has been left on for.
However, is it possible to trigger a script based on whether a device has been left on for, say, an hour, rather than having a script running every few minutes to check this?
Thanks
Yes this is possible. Using the "easy" way with cancelQueuedCommands and afterSec() methods. Works most of the times but not if domoticz or system is restarted between time device was switched On and scheduled Off time. (See first script)

and a somewhat more complicated method where Off time is stored in persistent data and script to switch Off device is triggered by a delayed HTTP callback. Using this method it's possible to switch lights Off even after a system reboot or domoticz restart. (see 2nd script)

Both methods use a default maxOnSeconds that can be overruled by putting maxOnSeconds: xxxx in the device description field, where xxxx is the amount of seconds the device should stay max. on.

All devices to be considered can be put in the on = devices {} section; either as a list of devicenames or as (a list) of wildcarded devicenames.

Code: Select all

return {
    on = { devices = {"device1", "device2"}},     -- Devices to be checked (can be wildcarded names)
           
    logging = { level = domoticz.LOG_ERROR,    -- change to Debug when script does not do what's expected
                marker = "maxOn" },    

    execute = function(dz, item)
        local defaultMaxOnSeconds = 3600 -- default max on time in seconds
 
        local function logWrite(str,level)
            dz.log(tostring(str),level or dz.LOG_DEBUG)
        end
        
        local function deviceMaxOnSeconds(device)
            local maxOnSeconds
            if device.description ~= nil and device.description ~= "" then
                _, _, maxOnSeconds = string.find(device.description,"maxOnSeconds:(%s*%d+)")       -- Check if the description field has "maxOnTime: " followed by an integer (max seconds)
                logWrite (device.name .. " max on time: " .. ( maxOnSeconds or defaultMaxOnSeconds ))
            end    
            return ( maxOnSeconds or defaultMaxOnSeconds )
        end

        item.cancelQueuedCommands()
        if item.active then
            logWrite(item.name .. ": " .. deviceMaxOnSeconds(item))
            item.switchOff().silent().afterSec(deviceMaxOnSeconds(item))
        end
    end
}

Code: Select all

scriptVar = "maxOndevices"

return {
    on = { timer   = { "at *:17" },               -- Additional check once every hour to ensure no devices are left behind after system boot or domoticz restart
           httpResponses = { scriptVar .. '*' },
           devices = {"device1", "device2"}},     -- Devices to be checked (can be wildcarded names)
           
    logging = { level = domoticz.LOG_ERROR,    -- change to Debug when script does not do what's expected
                marker = "maxOn" },    

    data = { activeDevices = { initial = {}}},
    
    execute = function(dz, item)
        local defaultMaxOnSeconds = 3600 -- default max on time in seconds
 
        local function logWrite(str,level)
            dz.log(tostring(str),level or dz.LOG_DEBUG)
        end
        
        local function deviceMaxOnSeconds(device)
            local maxOnSeconds
            if device.description ~= nil and device.description ~= "" then
                _, _, maxOnSeconds = string.find(device.description,"maxOnSeconds:(%s*%d+)")       -- Check if the description field has "maxOnTime: " followed by an integer (max seconds)
                logWrite (device.name .. " max on time: " .. ( maxOnSeconds or defaultMaxOnSeconds ))
            end    
            return ( maxOnSeconds or defaultMaxOnSeconds )
        end

        local function scheduleStillActiveCheck(device,delay)
            local url = dz.settings['Domoticz url'] .. "/json.htm?type=command&param=addlogmessage&message=" .. 
                        dz.utils.urlEncode(device.name .. " will be switched Off; if still active")
            dz.openURL({
                        url = url,
                        method = 'GET',
                        callback = scriptVar .. "_" .. device.idx 
                    }).afterSec(delay)    
        end

        if item.isDevice and not item.active then
            dz.data.activeDevices[item.idx] = nil
        elseif item.isDevice and item.active then 
            local maxOnSeconds = deviceMaxOnSeconds(item)
            logWrite(item.name .. " is Active for max " .. maxOnSeconds .. " seconds.")
            scheduleStillActiveCheck(item,maxOnSeconds)
            dz.data.activeDevices[item.idx] = dz.time.dDate + maxOnSeconds
        elseif item.isTimer or item.isHTTPResponse then
            for key, value in pairs(dz.data.activeDevices) do
                if dz.devices(key).active and ( dz.devices(key).lastUpdate.secondsAgo <= dz.data.activeDevices[key] ) then
                    dz.data.activeDevices[key] = nil
                    dz.devices(key).switchOff().silent()
                end
            end
        end
    end
}
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
geertvercamer
Posts: 84
Joined: Friday 12 May 2017 20:03
Target OS: -
Domoticz version:
Contact:

Re: Run script if device left on for a period of time?

Post by geertvercamer »

@Waaren,

I'm using this script to check if doors/windows are left open longer than wanted.

I don't understand this line of code:

Code: Select all

                if dz.devices(key).active and ( dz.devices(key).lastUpdate.secondsAgo <= dz.data.activeDevices[key] ) then
You add 'dz.time.dDate + maxOnSeconds', so it will take a whole lot of time for lastUpdate.secondsAgo to become bigger, no?

once the device triggers, the program waits for 'delay' seconds to launch the url. Isn't it enough to just check if the device is active?

Thanks
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Run script if device left on for a period of time?

Post by waaren »

geertvercamer wrote: Thursday 11 February 2021 20:57 once the device triggers, the program waits for 'delay' seconds to launch the url. Isn't it enough to just check if the device is active?
If you are sure that in between the device have not been switched Off and back On that would be enough.
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
geertvercamer
Posts: 84
Joined: Friday 12 May 2017 20:03
Target OS: -
Domoticz version:
Contact:

Re: Run script if device left on for a period of time?

Post by geertvercamer »

ah, ok, I see why now but still not how.
suppose maxOnSeconds = 60


dz.data.activeDevices[item.idx] = dz.time.dDate + maxOnSeconds

this sets quite big a value, all the seconds since 01/01/1970, so over 51 years in seconds

if dz.devices(key).active and ( dz.devices(key).lastUpdate.secondsAgo <= dz.data.activeDevices[key] ) then

secondsAgo is a duration, right? So this test is only false if my door would be open for more then 51 years, 1 month, 13 days and 10 hours

shouldn't you compare the lastUpdate timestamp in seconds to the dz.data.activeDevices[key]?

Or am i missing something completely here?
Thanks!
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Run script if device left on for a period of time?

Post by waaren »

geertvercamer wrote: Saturday 13 February 2021 10:04 ah, ok, I see why now but still not how.
Or am i missing something completely here?
Probably because there were still some bugs in the script :oops:

I fixed them in below version and added some debug log lines to make it more clear what happens

Code: Select all

local scriptVar = 'maxOndevices' 

return
{
    on =
    {
        timer  = -- Additional check once every hour to ensure no devices are left behind after system boot or domoticz restart
        {
            'at *:17' ,
            'every minute',  -- for debug only
        },
        httpResponses =
        {
            scriptVar .. '*',
        },
        devices =   -- Devices to be checked (can be wildcarded names)
        {
            'device1' ,
            'device2' ,
        },
    },

    logging =
    {
        level = domoticz.LOG_DEBUG,    -- change to Debug when script does not do what's expected
        marker = 'maxOn',
    },

    data =
    {
        activeDevices =
        {
            initial = {}
        }
    },

    execute = function(dz, item)
        local defaultMaxOnSeconds = 3600 -- default max on time in seconds

        local function deviceMaxOnSeconds(device)
            local maxOnSeconds
            if device.description ~= nil and device.description ~= '' then
               maxOnSeconds = device.description:match('maxOnTime:(%s*%d+)') -- Check if the description field has 'maxOnTime: int' (in seconds)
               dz.log (device.name .. ' max on time: ' .. ( maxOnSeconds or defaultMaxOnSeconds ), dz.LOG_DEBUG)
            end
            return ( maxOnSeconds or defaultMaxOnSeconds )
        end

        local function scheduleStillActiveCheck(device,delay)
            local url = dz.settings['Domoticz url'] .. '/json.htm?type=command&param=addlogmessage&message=' ..
                        dz.utils.urlEncode(device.name .. ' will be switched Off; if still active')
            dz.openURL({
                        url = url,
                        method = 'GET',
                        callback = scriptVar .. '_' .. device.idx
                    }).afterSec(delay + 1)
        end

        if item.isDevice and not item.active then
            dz.data.activeDevices[item.idx] = nil
        elseif item.isDevice and item.active then
            local maxOnSeconds = deviceMaxOnSeconds(item)
            dz.log(item.name .. ' is activated for max ' .. maxOnSeconds .. ' seconds.', dz.LOG_DEBUG)
            scheduleStillActiveCheck(item, maxOnSeconds)
            dz.data.activeDevices[item.idx] = os.time() + maxOnSeconds
        elseif item.isTimer or item.isHTTPResponse then
            for key, value in pairs(dz.data.activeDevices) do

                dz.log('Comparing ' .. dz.devices(key).name .. ' dz.data: ' .. math.floor(dz.data.activeDevices[key]) .. ' against ' .. os.time(), dz.LOG_DEBUG)

                if dz.devices(key).active and dz.data.activeDevices[key] < os.time() then
                    dz.log( 'Switching off ' .. dz.devices(key).name , dz.LOG_DEBUG)
                    dz.data.activeDevices[key] = nil
                    dz.devices(key).switchOff().silent()
                elseif dz.devices(key).active then
                    dz.log(dz.devices(key).name .. ' can be left on for another ' .. math.floor( dz.data.activeDevices[key] - os.time() ) .. ' seconds' , dz.LOG_DEBUG)
                else
                    dz.log( 'No action needed for ' .. dz.devices(key).name .. ' needed. It is already Off. ', dz.LOG_DEBUG)
                end
            end
        end
    end
}
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
EddyG
Posts: 1042
Joined: Monday 02 November 2015 5:54
Target OS: -
Domoticz version:

Re: Run script if device left on for a period of time?

Post by EddyG »

Tnx, for the script. I have a few issues.
In the logging I find.

Code: Select all

Debug: maxOn: OpenURL: callback = waterpeilGrebbe_38
That callback is from an other script. So it looks like the "scriptVar" is used from an other script.
Also I found that

Code: Select all

 local url = dz.settings['Domoticz url'] .. '/json.htm?type=command&param=addlogmessage&message=' ..
The original is logged as:

Code: Select all

Debug: maxOn: OpenURL: url = http://0.0.0.0:8080/json.htm?
It is not working for me, I had to use hardcoded "http://127.0.0.1:8080" for the url
Any ideas?
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Run script if device left on for a period of time?

Post by waaren »

EddyG wrote: Saturday 13 February 2021 12:19

Code: Select all

Debug: maxOn: OpenURL: callback = waterpeilGrebbe_38
That callback is from an other script. So it looks like the "scriptVar" is used from an other script.
Change

Code: Select all

scriptVar = 
to

Code: Select all

local scriptVar = 
Also I found that

Code: Select all

 local url = dz.settings['Domoticz url'] .. '/json.htm?type=command&param=addlogmessage&message=' ..
It is not working for me, I had to use hardcoded "http://127.0.0.1:8080" for the url
Did you set your location and
Local Networks (no username/password):

in the settings ?
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
EddyG
Posts: 1042
Joined: Monday 02 November 2015 5:54
Target OS: -
Domoticz version:

Re: Run script if device left on for a period of time?

Post by EddyG »

Yes, stupid me. The "local" worked of course.
Yes, mylocation is set (from the initial start of my Domoticz), local network like 127.0.0.* and a few in 192.168.2.* even Domoticz it self.
But it comes back with 0.0.0.0
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Run script if device left on for a period of time?

Post by waaren »

EddyG wrote: Saturday 13 February 2021 19:38 Yes, stupid me. The "local" worked of course.
Yes, mylocation is set (from the initial start of my Domoticz), local network like 127.0.0.* and a few in 192.168.2.* even Domoticz it self.
But it comes back with 0.0.0.0
Can you try and change it to below line? That will take care of an IPv6 type of address.

Code: Select all

127.0.0.*;192.168.2.*;::1
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Run script if device left on for a period of time?

Post by waaren »

waaren wrote: Saturday 13 February 2021 20:26
EddyG wrote: Saturday 13 February 2021 19:38 Yes, stupid me. The "local" worked of course.
Yes, mylocation is set (from the initial start of my Domoticz), local network like 127.0.0.* and a few in 192.168.2.* even Domoticz it self.
But it comes back with 0.0.0.0
Can you try and change it to below line? That will take care of an IPv6 type of address.

Code: Select all

127.0.0.*;192.168.2.*;::1
If that does not work could it be that you start domoticz with the option -wwwbind 0.0.0.0 ?
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
EddyG
Posts: 1042
Joined: Monday 02 November 2015 5:54
Target OS: -
Domoticz version:

Re: Run script if device left on for a period of time?

Post by EddyG »

Sorry for the delay.
I already have IPv6 added and I do not use -wwwbind 0.0.0.0 to start domoticz.
But my workaround of hardcode 127.0.0.1 works without errors.
Tnx.
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Run script if device left on for a period of time?

Post by waaren »

EddyG wrote: Monday 15 February 2021 14:40 Sorry for the delay.
I already have IPv6 added and I do not use -wwwbind 0.0.0.0 to start domoticz.
But my workaround of hardcode 127.0.0.1 works without errors.
OK. Can you please add

Code: Select all

utils.log(_gv, utils.LOG_FORCE)
at line 38 of <domoticz dir>/dzVents/runtime/EventHelpers.lua for at least a minute and share te results here?

It might help in identifying what causes your issue and if it can be solved it could prevent the same for others. Thx !
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
EddyG
Posts: 1042
Joined: Monday 02 November 2015 5:54
Target OS: -
Domoticz version:

Re: Run script if device left on for a period of time?

Post by EddyG »

I did that and now I see in the logging ->

Code: Select all

 ["domoticz_wwwbind"]="0.0.0.0" 
I restarted Domoticz and I also see:

Code: Select all

Status: WebServer(HTTP) started on address: 0.0.0.0 with port 8080
Status: WebServer(SSL) started on address: 0.0.0.0 with port 443
Status: Camera: settings (re)loaded
Starting shared server on: 0.0.0.0:6144
In /etc/init.d/domoticz.sh there is NO parameter of wwwbind
This is the partial output of 'sudo systemctl status domoticz.service'

Code: Select all

  Process: 836 ExecStart=/etc/init.d/domoticz.sh start (code=exited, status=0/SUCCESS)
    Tasks: 39 (limit: 4915)
   CGroup: /system.slice/domoticz.service
           └─1338 /home/pi/domoticz/domoticz -daemon -www 8080 -loglevel info -log /var/log/domoticz.log -dbase /home/pi/domoticz/domoticz.db
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Run script if device left on for a period of time?

Post by waaren »

EddyG wrote: Monday 15 February 2021 16:20 I did that and now I see in the logging ->

Code: Select all

 ["domoticz_wwwbind"]="0.0.0.0" 
OK I guess somewhere in your config the wwwbind is set to 0.0.0.0 but I have no idea where to search for it.
Until you find it, probably the easiest is to just keep using your work around.
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
geertvercamer
Posts: 84
Joined: Friday 12 May 2017 20:03
Target OS: -
Domoticz version:
Contact:

Re: Run script if device left on for a period of time?

Post by geertvercamer »

Code: Select all

dz.data.activeDevices[key] < os.time()
won't this always give true?
Last edited by geertvercamer on Tuesday 16 February 2021 16:15, edited 1 time in total.
User avatar
waaren
Posts: 6028
Joined: Tuesday 03 January 2017 14:18
Target OS: Linux
Domoticz version: Beta
Location: Netherlands
Contact:

Re: Run script if device left on for a period of time?

Post by waaren »

geertvercamer wrote: Monday 15 February 2021 17:43

Code: Select all

dz.data.activeDevices[key] < os.time()
won't this always give true?
No because you set it at an earlier stage to
os.time() + maxOnSeconds

So it it will only become true after maxOnSeconds seconds passed by since it was set.
Debian buster, bullseye on RPI-4, Intel NUC.
dz Beta, Z-Wave, RFLink, RFXtrx433e, P1, Youless, Hue, Yeelight, Xiaomi, MQTT
==>> dzVents wiki
geertvercamer
Posts: 84
Joined: Friday 12 May 2017 20:03
Target OS: -
Domoticz version:
Contact:

Re: Run script if device left on for a period of time?  [Solved]

Post by geertvercamer »

I think I get it. The function scheduleStillActiveCheck is called which fires the URL after x seconds, but doesn't wait for maxOnSeconds + 1 to return.
Thanks waaren!
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest