Page 3 of 3

Re: Generic auto-off

Posted: Monday 24 February 2020 14:30
by Vondee
For testing I shortened the time to set the level to 6 minutes:

"auto_off_dimlevel": { "level": 20, "minutes": 6 },
"auto_off_motion_device": ["Keuken Sensor PIR"]

At 23:42:29 the light was switched off manually, level was still 20%
At 23:42:52 PIR went to 'Off'

Lightbulb got the commands:
23:49:00 Set Level: 20%
23:49:00 Off
23:49:01 Set Level: 20%

Seems that the lightbulb get more commands and the last one is 'Set Level'

Re: Generic auto-off

Posted: Friday 20 March 2020 11:43
by rrozema
I have the script with the dimming feature still running in my home since I wrote it in January and I haven't noticed any odd behavior. I do have an older beta domoticz with an older dzVents version 2.5.0. I'm a little scared to update Domo since it works now and I have had some pretty bad experiences in the past. But the script only sends at most one command per run to any light, so I have no idea where this extra command you're seeing could come from. It may be something with the script being triggered multiple times, but as I said: I don't experience this behavior so I can't do much to help you. Sorry. I will try to revisit this update soon and add more new features as I was planning.

Re: Generic auto-off

Posted: Wednesday 23 September 2020 20:57
by tbryel
Hi rrozema,

First of all thanks a lot this really is a great addition. For myself i was looking for a solution that would allow me to auto shut down my network printer after i've used it and therefore needed to turn it on (45 minutes).

I spend some time getting my first dzvents script set and showing in the logs (i'm on a synology) but now the scripts seem to trigger correctly as they are appearing in the logs. I've set the printer to auto shut down after 2 minutes by maintaining the following description:

Code: Select all

{
"auto_off_minutes": 2
}
The logs show the following which seem to tell me that something is wrong with the description i've set above. But honestly I can't see what's wrong in the description.

Logs:

Code: Select all

2020-09-23 20:46:00.363 Status: dzVents: Info: Generic Auto Off v2.03: ------ Start external script: autoOff.lua:, trigger: every minute
2020-09-23 20:46:00.363 Status: dzVents: Info: Generic Auto Off v2.03: Generic Auto Off v2.03, Domoticz v4.10717, Dzvents v2.4.19.
2020-09-23 20:46:00.416 Status: dzVents: Info: Generic Auto Off v2.03: Device description for XBMC (keuken) is not in json format. Ignoring this device.
2020-09-23 20:46:00.440 Status: dzVents: Info: Generic Auto Off v2.03: Device description for Printer (zolder) is not in json format. Ignoring this device.
2020-09-23 20:46:00.440 Status: dzVents: Info: Generic Auto Off v2.03: Scanned 21 devices.
2020-09-23 20:46:00.440 Status: dzVents: Info: Generic Auto Off v2.03: ------ Finished autoOff.lua
I've downloaded your script from github (latest version: https://github.com/rrozema/Domoticz-scripts)

Ps. I'm running domoticz on Synology (Jadahl package) not sure if that has anything to do with this

Thank you for your help.

Kind regards,

Tim

Re: Generic auto-off

Posted: Thursday 24 September 2020 9:44
by tbryel
Hi

I tried a couple more things this morning. First thing I did was reverting the auto-Off lua script to an older version found in this thread. Issue persisted in the same way.

Then I turned on dzvents logs in full trying to find what description value is getting set. It returned the following information which seems to be incorrect:

Code: Select all

	[21] = {
		["rawData"] = {
			[1] = "0";
		};
		["lastLevel"] = 0;
		["baseType"] = "device";
		["name"] = "Printer";
		["switchTypeValue"] = 0;
		["description"] = "{\
\"auto_off_minutes\": 2\
}";
		["deviceType"] = "Light/Switch";
		["subType"] = "Switch";
		["changed"] = false;
		["timedOut"] = false;
		["data"] = {
			["usedByCamera"] = false;
			["hardwareID"] = 7;
			["levelVal"] = 0;
			["_nValue"] = 1;
			["maxDimLevel"] = 100;
			["icon"] = "lightbulb";
			["hardwareType"] = "OpenZWave USB";
			["hardwareTypeValue"] = 21;
			["hardwareName"] = "Z-Stick GEN5";
			["unit"] = 1;
			["protected"] = false;
			["_state"] = "On";
		};
		["switchType"] = "On/Off";
		["batteryLevel"] = 255;
		["id"] = 149;
		["lastUpdate"] = "2020-09-24 09:14:27";
		["deviceID"] = "00001001";
		["signalLevel"] = 12;
	};
The description shows a set of back slashes '\'. These backward slashes seem to be used to indicate a 'line break' however there isn't a line break set at the end of auto_off_minutes in the description field, still it shows a backslash in the log.

Code: Select all

["description"] = "{\
\"auto_off_minutes\": 2\
}";

I'm not sure if there is any other way of debugging the script to allow me to find out why it's failing. If someone would be able to help me by validating if these 'backslashes' are also there in their dzvents logs (domoticzData.lua) that would be really appreciated.

I'm running the following domoticz versions:
4.10717
dzVents Version: 2.4.19

Re: Generic auto-off

Posted: Thursday 24 September 2020 11:16
by waaren
tbryel wrote: Thursday 24 September 2020 9:44 The description shows a set of back slashes '\'. These backward slashes seem to be used to indicate a 'line break' however there isn't a line break set at the end of auto_off_minutes in the description field, still it shows a backslash in the log.
Can try changing the description to

Code: Select all

{"auto_off_minutes": 2}

Re: Generic auto-off

Posted: Thursday 24 September 2020 12:20
by tbryel
Hi Waaren,

Thank you for your reply. I tried as per your suggestion but the response is unfortunatly still the same. The logs show:

Code: Select all

2020-09-24 12:18:00.648 Status: dzVents: Error (2.4.19): Device description for Printer is not in json format. Ignoring this device.
The response in DomoticzData.lua now shows the following (there are still back slashes in the description):

Code: Select all

["description"] = "{\"auto_off_minutes\": 2}";

Would you be able to have a look at your DomoticzData.lua to see if you also see backslashes included? Migth be this is the standard behavior but the "\" shown at the end of the statement "auto_off_minutes\" I feel might be causing the script not to get triggered and respond with "not in valid json format". Maybe i'm totally looking in the wrong place though.

Re: Generic auto-off

Posted: Thursday 24 September 2020 14:46
by waaren
tbryel wrote: Thursday 24 September 2020 12:20 Would you be able to have a look at your DomoticzData.lua to see if you also see backslashes included? Migth be this is the standard behavior but the "\" shown at the end of the statement "auto_off_minutes\" I feel might be causing the script not to get triggered and respond with "not in valid json format". Maybe i'm totally looking in the wrong place though.
I see the same but this is as expected as the double quotes in the string should be escaped.
To help me check can you share the script you use now via PM. It is not clear to me witch version of the script you use.
Also what is your current domoticz / dzVents version?

Re: Generic auto-off

Posted: Thursday 24 September 2020 18:09
by rrozema
tbryel wrote: Wednesday 23 September 2020 20:57

Code: Select all

{
"auto_off_minutes": 2
}
To be honest, I don't see anything wrong with this either.
Ps. I'm running domoticz on Synology (Jadahl package) not sure if that has anything to do with this
I didn't think that this should make any difference, but I could be wrong: i'm running on raspberry pi.

Sorry I can't be of more help to you.


Actually: Just a wild guess: do you maybe have a second device named "Printer (zolder)" in your setup? Because the json you gave us seems to be perfectly valid.

Re: Generic auto-off

Posted: Friday 25 September 2020 16:10
by rrozema
Can you please in the auto-off script's code find the lines

Code: Select all

                    else
                        domoticz.log( 'Device description for '.. device.name ..' is not in json format. Ignoring this device.', domoticz.LOG_WARNING)
                    end
and add an extra line like this?

Code: Select all

                    else
                        domoticz.log( 'Device description for '.. device.name ..' is not in json format. Ignoring this device.', domoticz.LOG_WARNING)
                        domoticz.log( 'Device description for '.. device.name ..' = |' .. device.description ..'|', domoticz.LOG_ERROR)
                    end
This should now show in the log an error that shows us the contents of the description field that the script sees.

I'm beginning to suspect that you're getting these messages on other devices than those that you want to be switched off, is that correct?
If so, you should realise that the message "Device description for ... is not in json format. Ignoring this device." is just a warning to inform you that some of your devices can't be controlled by auto-off. This can be for multiple reasons. For example because you've got some descriptive text in the description field or you use another script that requires some other (non-json) text in the description field, or there is an typo in your json. The messages are not errors: the script just skips the mentioned devices, but it still functions for all your other devices. The message is merely there to help you in case you accidently mistyped your values in the device's description. If you switch the warning level off, the messages won't be logged any more.

However, if those 2 devices are the ones you tried to have switched off, please share the contents of the description field for that "XBMC (keuken)" device and for the "Printer (zolder)" device by adding the extra line to the script and then give us the error message from the log.

Re: Generic auto-off

Posted: Friday 25 September 2020 17:02
by tbryel
Hi rrozema,

I contacted Waaren via PM as per his request. He said it was due to my Domoticz version. I'm running a Domoticz stable version: 4.10717 (dzVents Version: 2.4.19)

He said he would have a look and would share me a changed script version which I received.

Code: Select all

--[[
        AutoOff without JSON

        This script will run every minute and can automatically send an 'Off' command to turn off any device after
        it has been on for some specified time. Each device can be individually configured by adding 
        somewhere in the description field of a device.

        <dzAutoOff>maxOnTime:17</dzAutoOff> -- 17 is an example; any integer can be entered 

-- ]]

return {
    on = 
    {
        timer = 
        {
            'every minute'
        },
    },

    logging = 
    {
        marker = 'AutoOff without JSON',
        level = domoticz.LOG_INFO, -- switch to domoticz.LOG_ERROR to silence the log or to domoticz.LOG_DEBUG for locating a potential issue
    },

    execute = function(dz, item)
        local counter = {}

        local function inc(value)
            counter[value] = counter[value] and counter[value] + 1 or 1
        end

        local function getMatchingDescription(description)
            if description == nil or description == '' then return false end
            open = '<dzAutoOff>'
            close = '</dzAutoOff>'
            return description:match(open .. "(.*)" .. close)
        end

        local function mustSwitchOff(minutes, control)
            return tonumber(control:match('maxOnTime:%s*(%d+)')) <= minutes
        end

        dz.devices().filter( function(dv) return dv.active end).forEach( function(dv)
            inc('allActiveDevices')
            local matchingDescription = getMatchingDescription(dv.description)
            if matchingDescription then
                inc('matchingDevices')
                local switchOff = mustSwitchOff((dv.lastUpdate.minutesAgo or math.maxInt), matchingDescription)
                if switchOff then
                    inc('timePassed')
                    dv.switchOff()
                    dz.log(dv.name .. ' is switched off because it has been on for > ' .. dv.lastUpdate.minutesAgo.. ' minutes.', dz.LOG_FORCE)
                end
            end
        end
        )

        dz.log('Scanned: ' .. ( counter.allActiveDevices or 0 ).. ' active devices.',dz.LOG_INFO)
        dz.log('Matching: ' .. ( counter.matchingDevices or 0) .. ' devices.',dz.LOG_INFO)
        dz.log('Max. on time passed: ' .. ( counter.timePassed or 0) .. ' devices.',dz.LOG_INFO)
    end
}

I tested this script and it works (does require setting up the device description differently than we would do using your script (versions)).

I can update and overwrite again with the change you suggested. Do you recommend doing that? Again, i'm not sure if the resolution and issue i've on my Domoticz version is of any help in improving the over-all script, i hope it is.

Kind regards,

Tim

Re: Generic auto-off

Posted: Friday 25 September 2020 17:43
by rrozema
I think you're trying to fix a non-existing problem: I suspect that either the message you're seeing is not from the devices that you're trying to switch off, or there's an error in those device's json text. My suggestion is to disable waaren's script and save it. Then add an additional new script and paste above code into that (you don't even have to name it: the default name "script #x" will do just as well. Then save this new script and wait a minute to see what the log shows and see what actual problem auto-off has with the value in your device's description fields. If you're done checking this, you can disable or delete the "Script #x" script and re-enable waaren's script if you want to.

But, the script that waaren gave you has a lot less functionality than auto-off has (f.e. i may have overlooked it in my quick scan of the code, but I did not see support for motion sensors). If I read the code correctly, it is merely to showcase an alternative way of sharing the description field of one device among multiple scripts, not to mimic all functionality of auto-off. However I've not heard from you that using multiple scripts for a single device was actually your problem. Plus, the proposed method has the same shortcoming that auto-off has: it still requires cooperation of the both script's authors to adhere to the convention that waaren suggested. If they have to cooperate anyway, both authors could just as well agree to both use json in the description field...

A real solution -allowing script writers to use 'script-specific' settings per device without interfering with other scripts' settings- would require Domoticz to expose a method that enables script authors to store these settings with each device in an different way than storing it as a text in the description field.

Re: Generic auto-off

Posted: Friday 25 September 2020 18:13
by tbryel
Hi rrozema,

It's not a non-existing issue. The description on my Domoticz version seems to be formatted differently by dzVents hence it's erroring out.

As per your suggestion i've updated the script and set it to the following (using the github version and extending the else):

Code: Select all

-- This script will run every minute and can automatically send an 'Off' command to turn off any device after
-- it has been on for some specified time. Each device can be individually configured by putting json coded 
-- settings into the device's description field. The settings currently supported are:
-- - "auto_off_minutes" : <time in minutes>
-- - "auto_off_motion_device" : "<name of a motion detection device>"
-- If "auto_off_minutes" is not set, the device will never be turned off by this script. If 
-- "auto_off_minutes" is set and <time in minutes> is a valid number, the device will be turned off when it 
-- is found to be on plus the device's lastUpdate is at least <time in minutes> minutes old. This behavior 
-- can be further modified by specifying a valid device name after "auto_off_motion_device". When a motion 
-- device is specified and the device's lastUpdate is at least <time in minutes> old, the device will not 
-- be turned off until the motion device is off and it's lastUpdate is also <time in minutes> old. 
-- Specifying "auto_off_motion_device" without specifying "auto_off_minutes" does nothing.
--
-- Example 1: turn off the device after 2 minutes:
-- {
-- "auto_off_minutes": 2
-- }
--
-- Example 2: turn off the device when it has been on for 5 minutes and no motion has been detected for 
-- at least 5 minutes:
-- {
-- "auto_off_minutes": 5,
-- "auto_off_motion_device": "Overloop: Motion"
-- }
--
-- Example 3: turn off the device when it has been on for 1 minute and not motion was detected for at least 1 
-- minute on either one of a set of motion sensors.
--{
--"auto_off_minutes": 1,
--"auto_off_motion_device": ["Overloop 1: Motion 1", "Overloop 1: Motion 2"]
--}

local AUTOOFFVERSION = '2.03'

return {
	on = {

		timer = {
			'every minute'
		}
	},
	logging = {
        level = domoticz.LOG_WARNING,
        marker = 'Generic Auto Off v' .. AUTOOFFVERSION
    },
	execute = function(domoticz, triggeredItem, info)
        local cnt = 0
        
        domoticz.log( 'Generic Auto Off v' .. AUTOOFFVERSION .. ', Domoticz v' .. domoticz.settings.domoticzVersion .. ', Dzvents v' .. domoticz.settings.dzVentsVersion .. '.', domoticz.LOG_INFO)
        
        local now = domoticz.time
        
        domoticz.devices().forEach(
	        function(device)
	            cnt = cnt + 1
	            local description
                local motion_device_names  -- If no settings are found we may still have to
                                           -- remove some device's motion devices from the 
                                           -- trigger lists. So we start with an empty list.

                motion_device_names = {}
                description = device.description

                if description ~= nil and description ~= '' then
                    local ok, settings = pcall( domoticz.utils.fromJSON, description)
                    if ok and settings ~= nil then
                        -- Determine highest level available in the settings that is 
                        -- lower than the current level where 'Off' equals level 0.
                        local dimlevel = nil;

                        -- Determine the list of motion devices configured for this
                        -- device in the settings.
                        if type(settings.auto_off_motion_device) == "string" then
                            table.insert( motion_device_names, settings.auto_off_motion_device)
                        elseif type(settings.auto_off_motion_device) == "table" then
                            for i,v in ipairs(settings.auto_off_motion_device) do
                                table.insert( motion_device_names, v)
                            end
                        end
                    
                        -- Lowest dim level is 'Off', this I will represent as level == 0.
                        -- If auto_off_minutes was specified and the device is not off, I 
                        -- will use this to initialise the dimlevel variable with a level 
                        -- of 0. If the device is off already, we don't need to change it.
                        if settings.auto_off_minutes ~= nil and device.bState then
                            dimlevel = { level = 0, minutes = settings.auto_off_minutes}
                        end
                        
                        -- If one or more dimlevels were specified, the user must think our
                        -- device is a dimmer switch, so it should be safe to reference
                        -- its level attribute. We search for the dimlevel that has the
                        -- highest level below that of our device's level.
                        if settings.auto_off_dimlevel ~= nil and type(settings.auto_off_dimlevel) == "table" then
                            -- if a single dimlevel was specified...
                            if settings.auto_off_dimlevel.level ~= nil then
                                if settings.auto_off_dimlevel.level < 0 then
                                    settings.auto_off_dimlevel.level = 0
                                elseif settings.auto_off_dimlevel.level > 100 then
                                    settings.auto_off_dimlevel.level = 100
                                end
                                    
                                if settings.auto_off_dimlevel.level < device.level then
                                    if dimlevel == nil or settings.auto_off_dimlevel.level > dimlevel.level then
                                        dimlevel = settings.auto_off_dimlevel
                                    end
                                end
                            else
                                -- or when multiple dimlevels were specified as a table of tables.
                                for i,v in ipairs(settings.auto_off_dimlevel) do
                                    if v.level < 0 then
                                        v.level = 0
                                    elseif v.level > 100 then
                                        v.level = 100
                                    end
                                    
                                    if v.level < device.level then
                                        if dimlevel == nil or v.level > dimlevel.level then
                                            dimlevel = v
                                        end
                                    end
                                end
                            end
                        end

                        -- If we have found a new dim level then see if it is time yet to
                        -- set this new level.
                        if dimlevel ~= nil then
                            local minutes = dimlevel.minutes
                            
                            -- Find the latest last modified date from our device plus
                            -- any motion devices that may have been specified. Initially
                            -- assume our device has the latest last modified date.
                            local lastUpdate = device.lastUpdate
                            
                            -- We will skip setting a new level if either of the following is true: 
                            -- at least one of the motion devices has state 'On' or the lastUpdate 
                            -- on our device or any of the specified motion devices is less than 
                            -- <minutes> ago. To accomplish this, lets find out if any of the 
                            -- motion devices has a more recent lastUpdate than that of our device.
                            -- If a motion device has state 'On', we will set lastUpdate to nil,
                            -- indicating we can skip the check for lastUpdate completely.
                            local motion_devices = domoticz.devices().filter(motion_device_names)
                            lastUpdate = motion_devices.reduce(
                                    function(acc, md)
                                        if md.timedOut ~= true then -- Ignore motion devices that have timed out, to 
                                                                    -- avoid leaving the light on because a sensor that 
                                                                    -- has an empty battery isn't updated to 'Off'.
                                            if acc ~= nil then      -- If a previous sensor was 'On', we will have set 
                                                                    -- lastUpdate to nil and we don't want to overwrite
                                                                    -- this.
                                                if md.bState then
                                                    --domoticz.utils._.print( 'Sensor ' .. md.name .. ' is on')
                                                    domoticz.log( 'Motion device ' .. md.name .. ' is On.', domoticz.LOG_DEBUG)

                                                    acc = nil               -- Set lastUpdate to nil to indicate at least 
                                                                            -- one sensor is 'On'.
                                                elseif md.lastUpdate.compare(acc).compare < 0 then  -- If acc < lastUpdate
                                                    domoticz.log( 'Motion device ' .. md.name .. ' was last modified ' .. md.lastUpdate.raw .. '.', domoticz.LOG_DEBUG)
                                                    acc = md.lastUpdate     -- We've found a more recent lastUpdate.
                                                end
                                            end
                                        else
                                            domoticz.log( 'Motion device ' .. md.name .. ' ignored because it timed out. Do you need to replace the battery?', domoticz.LOG_WARNING)
                                        end
                                        return acc -- Always return the accumulator.
                                    end, lastUpdate)

                            if lastUpdate ~= nil and lastUpdate.secondsAgo > tonumber(minutes * 60) then
                                if dimlevel.level > 0 then
            		                domoticz.log(device.name .. ' is dimmed to level ' .. dimlevel.level .. ' after ' .. dimlevel.minutes .. ' minutes.', domoticz.LOG_DEBUG)
                                    device.setLevel(tonumber(dimlevel.level))
                                else
            		                domoticz.log(device.name .. ' is switched off after ' .. dimlevel.minutes .. ' minutes.', domoticz.LOG_DEBUG)
                                    device.switchOff()
                                end
                            end
                        end
                    else
                        domoticz.log( 'Device description for '.. device.name ..' is not in json format. Ignoring this device.', domoticz.LOG_WARNING)
                        domoticz.log( 'Device description for '.. device.name ..' = |' .. device.description ..'|', domoticz.LOG_ERROR)
                    end
                end
            end
        )
    
        domoticz.log('Scanned ' .. tostring(cnt) .. ' devices.', domoticz.LOG_INFO)
	end
}
Description set on device and activated device:

Code: Select all

{
"auto_off_minutes": 2
}
result in log:

Code: Select all

2020-09-25 18:12:00.629 Status: dzVents: Debug: Generic Auto Off v2.03: Processing device-adapter for Printer (zolder): Switch device adapter
2020-09-25 18:12:00.630 Status: dzVents: Info: Generic Auto Off v2.03: Device description for Printer (zolder) is not in json format. Ignoring this device.
2020-09-25 18:12:00.631 Status: dzVents: Error (2.4.19): Generic Auto Off v2.03: Device description for Printer (zolder) = |{
2020-09-25 18:12:00.631 "auto_off_minutes": 2
2020-09-25 18:12:00.631 }|
2020-09-25 18:12:00.631 Status: dzVents: Info: Generic Auto Off v2.03: Scanned 21 devices.
2020-09-25 18:12:00.631 Status: dzVents: Info: Generic Auto Off v2.03: ------ Finished autoOff.lua

This is not working, it's still erroring out however the logs show more information then it did previously. But the device is still turned on.

Re: Generic auto-off

Posted: Saturday 26 September 2020 9:17
by rrozema
Ok, so we've established now that you entered the setting correctly, on the correct device, plus that dzvents correctly passes the description text to the script. However, the json parser in your version of domotic does not accept this description as valid json. Wouldn't it be easier that you upgrade your domoticz to a version that does have a working json parser? Auto-off won't be the only part affected by the old age of your Domoticz.

Re: Generic auto-off

Posted: Saturday 26 September 2020 15:02
by tbryel
Hi rrozema,

Yes exactly we already established that conclusion. I don’t want to upgrade to 2020 yet as this is a major release and apparently has a lot of new issues.

The version I’m on is a stable and currently supported version, nothing strange there right:

I'm running a Domoticz stable version: 4.10717 (dzVents Version: 2.4.19)

I discussed the same with Waaren and he said that he would like to use the version I’m on to do some testing in case required. Happy to do so.

I can upgrade as well but as said I rather not and then running a script that allows me to recognize the description on my existing version is best for me for now.

Thanks for your help and script it’s amazing work and really very much appreciated.

Re: Generic auto-off

Posted: Saturday 26 September 2020 17:37
by rrozema
Maybe waaren can find if in your version the /a working json parser can be called an alternative way. If so I can maybe make that call conditionally based on the domoticz version detected.

Re: Generic auto-off

Posted: Saturday 26 September 2020 20:17
by waaren
rrozema wrote: Saturday 26 September 2020 17:37 Maybe waaren can find if in your version the /a working json parser can be called an alternative way. If so I can maybe make that call conditionally based on the domoticz version detected.
Worked with @tbryel on this and found that the file JSON.lua was missing in his version. After downloading this file and copying it to a location where dzVents could find it, the JSON decode works as designed.

Re: Generic auto-off

Posted: Saturday 26 September 2020 20:41
by tbryel
With help of waaren fully resolved it. The issue is that the Jadahl synology package I used didn't include JSON.lua.

After fetching it here http://regex.info/code/JSON.lua and inserting it here "../domoticz/dzVents/runtime/JSON.lua " the script from github is now fully working as expected (I also inserted it in to the ../scripts/lua folder).

Thanks @waaren for your support and @rrozema as well great script, great efforts.

Re: Generic auto-off

Posted: Monday 17 May 2021 12:15
by pipiche
@rozema that is a really awesome stuff that you developed here. I was not expecting to get something like. Really well done.