This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

Remaining time or counter
#41
Hello, using this great script I would like to modify it with your help.

So far this script is executed with the detection object. In my case, this object, through another script, is just a 1-second toggle to correct issues that sometimes occur when the detector does not send 'no detection'. The output object is modified with a checkwrite so that it only sends it once.

On the other hand, I have a Lock object and I want to use it to lock the detector and this script.

I would like if it is possible that this Blocking object, apart from blocking the script, could reset the time to zero and not modify the output object..



Code:
-- Set output address
AddressOutput = '39/0/1' -- 01. 1 bit (boolean)

--ID Detector Zone
i = 1

-- Set input address
AddressInput = '9/1/'..i -- 01. 1 bit (boolean)

-- Set external time address (optional)
AddressExternalTime = '38/1/'..i -- 07. 2 byte unsigned integer

-- Set feedback adress of time left indication (optional)
AddressTimeLeft = '38/2/'..i -- 255 byte string

-- Seconds or Minutes
SetSec = '38/3/'..i -- Set to false for Minutes or true for seconds

-- Set blocking address
AddressBlocking = '38/4/'..i -- 01. 1 bit (boolean)

-- Use time left indication
UseTimeLeft = true -- Set to false if no time left indication is used

-- Set time delay (Used when external time is not available)
SetDelay = 120 -- 2 minutes

-- Set factor delay (Multiplies Delay)
SetFac = 1

-- Logic can be turned of by value 0
Off_by_Value_Zero = false




-- ************************************** END PARAMETERS ************************************ --
-- *************************** DON'T CHANGE ANYTHING UNDER THIS LINE ************************ --


Input_Value = event.getvalue()
if Off_by_Value_Zero == false and (Input_Value == false or Input_Value == 0) then
return
else
tpid = storage.get('PID:' .. _SCRIPTNAME)
if tpid == nil then
     pid = os.getpid()
     storage.set('PID:' .. _SCRIPTNAME, pid)
else
   pid = os.getpid()  
   storage.set('PID:' .. _SCRIPTNAME, pid)
   os.kill(tpid, signal.SIGKILL)
end
 
blocking = grp.getvalue(AddressBlocking)
if not blocking then
   return
end

 
function Calculate_Time(StairCaseTime)
   if StairCaseTime > (86400 -1) then
     StairCaseTime = (86400 -1)
   end
   local hours = math.floor(StairCaseTime / 3600 % 24)
   local minutes = math.floor(StairCaseTime / 60 % 60)
   local seconds = math.floor(StairCaseTime % 60)
   time = {
     day = 0,
     hour = hours,
     minute = minutes,
     second = seconds,
   }
   time =string.format('%02d:%02d:%02d',time.hour,time.minute,time.second)
   return time
end
if Input_Value == true or Input_Value == 1 then
   ValueExternalTime = grp.getvalue(AddressExternalTime) or SetDelay
   if grp.getvalue(SetSec) then
    Multiply_Seconds = 1
   else
     Multiply_Seconds = 60
    end
   StairCaseTime = ValueExternalTime * Multiply_Seconds * SetFac
   grp.checkwrite(AddressOutput, true)
   if UseTimeLeft == true then
     if StairCaseTime > 0 then
       time = Calculate_Time(StairCaseTime)
       grp.write(AddressTimeLeft, time)
       repeat
         StairCaseTime = StairCaseTime - 1
         time = Calculate_Time(StairCaseTime)
         grp.write(AddressTimeLeft, time)
         os.sleep(1)
       until StairCaseTime == 0
     end   
   else
     os.sleep(StairCaseTime)
   end
   grp.write(AddressOutput, false)
elseif Input_Value == false or Input_Value == 0 then
   ValueOutput = grp.getvalue(AddressOutput)
   if ValueOutput == false then
     if UseTimeLeft == true then
       time = Calculate_Time(0)
       grp.write(AddressTimeLeft, time)
     end
     grp.write(AddressOutput, false)
   end
end
storage.delete('PID:' .. _SCRIPTNAME)
end
Reply
#42
Well, after a lot of trial/error, I got the result.

I think it can surely be simplified, so any suggestions are welcome.

This is triggered by events assigning the same tag on the input object and the lock object.

Every time the input object is activated (I do it with a 1 second toggle), the parameterized time starts. Activating the lock, locks the function, does not modify the output object, resets the timer, and removes it from storage.


Code:
-- Set output address
AddressOutput = '39/0/1' -- 01. 1 bit (boolean)

--ID Detector Zone
i = 1

-- Set input address
AddressInput = '9/1/'..i -- 01. 1 bit (boolean)

-- Set external time address (optional)
AddressExternalTime = '38/1/'..i -- 07. 2 byte unsigned integer

-- Set feedback adress of time left indication (optional)
AddressTimeLeft = '38/2/'..i -- 255 byte string

-- Seconds or Minutes
SetSec = '38/3/'..i -- Set to false for Minutes or true for seconds

-- Set blocking address
AddressBlocking = '38/4/'..i -- 01. 1 bit (boolean)

-- Use time left indication
UseTimeLeft = true -- Set to false if no time left indication is used

-- Set time delay (Used when external time is not available)
SetDelay = 120 -- 2 minutes

-- Set factor delay (Multiplies Delay)
SetFac = 1

-- Logic can be turned of by value 0
Off_by_Value_Zero = not grp.getvalue(AddressBlocking)




-- ************************************** END PARAMETERS ************************************ --
-- *************************** DON'T CHANGE ANYTHING UNDER THIS LINE ************************ --


Input_Value = grp.getvalue(AddressInput)
if Off_by_Value_Zero == false and (Input_Value == false or Input_Value == 0) then
return
else
tpid = storage.get('PID:' .. _SCRIPTNAME)
if tpid == nil then
     pid = os.getpid()
     storage.set('PID:' .. _SCRIPTNAME, pid)
else
   pid = os.getpid()   
   storage.set('PID:' .. _SCRIPTNAME, pid)
   os.kill(tpid, signal.SIGKILL)
end
 
blocking = grp.getvalue(AddressBlocking)
if not blocking then
    grp.checkwrite(AddressTimeLeft, '00:00:00')
    storage.delete('PID:' .. _SCRIPTNAME)
   return
end

 
function Calculate_Time(StairCaseTime)
   if StairCaseTime > (86400 -1) then
     StairCaseTime = (86400 -1)
   end
   local hours = math.floor(StairCaseTime / 3600 % 24)
   local minutes = math.floor(StairCaseTime / 60 % 60)
   local seconds = math.floor(StairCaseTime % 60)
   time = {
     day = 0,
     hour = hours,
     minute = minutes,
     second = seconds,
   }
   time =string.format('%02d:%02d:%02d',time.hour,time.minute,time.second)
   return time
end
if Input_Value == true or Input_Value == 1 then
   ValueExternalTime = grp.getvalue(AddressExternalTime) or SetDelay
   if grp.getvalue(SetSec) then
    Multiply_Seconds = 1
   else
     Multiply_Seconds = 60
    end
   StairCaseTime = ValueExternalTime * Multiply_Seconds * SetFac
   grp.checkwrite(AddressOutput, true)
   if UseTimeLeft == true then
     if StairCaseTime > 0 then
       time = Calculate_Time(StairCaseTime)
       grp.write(AddressTimeLeft, time)
       repeat
         StairCaseTime = StairCaseTime - 1
         time = Calculate_Time(StairCaseTime)
         grp.write(AddressTimeLeft, time)
         os.sleep(1)
       until StairCaseTime == 0
     end   
   else
     os.sleep(StairCaseTime)
   end
   grp.write(AddressOutput, false)
elseif Input_Value == false or Input_Value == 0 then
   ValueOutput = grp.getvalue(AddressOutput)
   if ValueOutput == false then
     if UseTimeLeft == true then
       time = Calculate_Time(0)
       grp.write(AddressTimeLeft, time)
     end
     grp.write(AddressOutput, false)
   end
end
storage.delete('PID:' .. _SCRIPTNAME)
end
Reply
#43
Hi,
It asked above but i cannot find an answer.
With an additional reset group addres, can we stop the script and make remaning time max . We need to stop process during countdown with an additional group address
Reply
#44
Can you describe your task in more detail? There's a similar script that can be adapted: https://forum.logicmachine.net/showthrea...5#pid26505
Reply
#45
(09.08.2022, 08:20)admin Wrote: Can you describe your task in more detail? There's a similar script that can be adapted: https://forum.logicmachine.net/showthrea...5#pid26505

Hi , I am looking a non residential script solution. I need 200 timer like in 1 LM and it's impossible to do with zero timer residential script. 
I need an update version of Mr. Erwin's script. It's event script. I need another event script which kill immediately main script which has some ossleep timer function.

Regards
Reply
#46
Single resident script that I've posted can handle multiple timers.

Erwin's script saves process ID into storage. You can stop the script like this, change scriptname as needed:
Code:
scriptname = 'my_event_script'

key = 'PID:' .. scriptname
pid = storage.get(key)
if pid then
  os.kill(pid, signal.SIGKILL)
  storage.delete(key)
end
But this approach is not 100% correct though because in some rare cases several event script instances can run in parallel.
Reply
#47
(09.08.2022, 10:09)It worked. Thanks a lotadmin Wrote: Single resident script that I've posted can handle multiple timers.

Erwin's script saves process ID into storage. You can stop the script like this, change scriptname as needed:
Code:
scriptname = 'my_event_script'

key = 'PID:' .. scriptname
pid = storage.get(key)
if pid then
  os.kill(pid, signal.SIGKILL)
  storage.delete(key)
end
But this approach is not 100% correct though because in some rare cases several event script instances can run in parallel.
Reply
#48
When i try to run this scrip it says: "2023.05.26 21:38:52","stairc","Resident script:38: attempt to index global 'event' (a nil value)
stack traceback:"

And can someone please post the newest script? there is so many variations in here!
Reply
#49
Post your full script.
Reply
#50
(26.05.2023, 19:46)Novodk Wrote: When i try to run this scrip it says: "2023.05.26 21:38:52","stairc","Resident script:38: attempt to index global 'event' (a nil value)
stack traceback:"

And can someone please post the newest script? there is so many variations in here!

Code:
-- ** Staircase logic with external time object and retriggering on input object Version 3.2 ** --
-- ****************************** Created by Erwin van der Zwart **************************** --
-- ************************************** SET PARAMETERS ************************************ --

-- Set input address
AddressInput = '4/0/1' -- 01. 1 bit (boolean)
-- Set output address
AddressOutput = '32/1/6' -- 01. 1 bit (boolean)

-- Set external time address (optional)
AddressExternalTime = '55/1/4' -- 07. 2 byte unsigned integer

-- Use time left indication
UseTimeLeft = true -- Set to false if no time left indication is used

-- Set feedback adress of time left indication (optional)
AddressTimeLeft = '32/1/5' -- 255 byte string

-- Set blocking address
AddressBlocking = '32/1/7'

-- Set time delay (Used when external time is not available)
SetDelay = 20 -- 2 minutes

-- Seconds or Minutes
SetSec = true -- Set to false for Minutes

-- Set factor delay (Multiplies Delay)
SetFac = 1

-- Logic can be turned of by value 0
Off_by_Value_Zero = false

-- ************************************** END PARAMETERS ************************************ --
-- *************************** DON'T CHANGE ANYTHING UNDER THIS LINE ************************ --

Input_Value = event.getvalue()
if Off_by_Value_Zero == false and (Input_Value == false or Input_Value == 0) then
return
else
tpid = storage.get('PID:' .. _SCRIPTNAME)
if tpid == nil then
     pid = os.getpid()
     storage.set('PID:' .. _SCRIPTNAME, pid)
else
   pid = os.getpid()   
   storage.set('PID:' .. _SCRIPTNAME, pid)
   os.kill(tpid, signal.SIGKILL)
end

blocking = grp.getvalue(AddressBlocking)
if not blocking then
   return
end

function Calculate_Time(StairCaseTime)
   if StairCaseTime > (86400 -1) then
     StairCaseTime = (86400 -1)
   end
   local hours = math.floor(StairCaseTime / 3600 % 24)
   local minutes = math.floor(StairCaseTime / 60 % 60)
   local seconds = math.floor(StairCaseTime % 60)
   time = {
     day = 0,
     hour = hours,
     minute = minutes,
     second = seconds,
   }
   time =string.format('%02d:%02d:%02d',time.hour,time.minute,time.second)
   return time
end
if Input_Value == true or Input_Value == 1 then
   ValueExternalTime = grp.getvalue(AddressExternalTime) or SetDelay
   if SetSec == true then
     Multiply_Seconds = 1
   else
     Multiply_Seconds = 60
   end
   StairCaseTime = ValueExternalTime * Multiply_Seconds * SetFac
   ValueOutput = grp.getvalue(AddressOutput)
   if ValueOutput == false then
     grp.write(AddressOutput, true)
   end
   if UseTimeLeft == true then
     if StairCaseTime > 0 then
       time = Calculate_Time(StairCaseTime)
       grp.update(AddressTimeLeft, time)
       repeat
         StairCaseTime = StairCaseTime - 1
         time = Calculate_Time(StairCaseTime)
         grp.update(AddressTimeLeft, time)
         os.sleep(1)
       until StairCaseTime == 0
     end   
   else
     os.sleep(StairCaseTime)
   end
   grp.write(AddressOutput, false)
elseif Input_Value == false or Input_Value == 0 then
   ValueOutput = grp.getvalue(AddressOutput)
   if ValueOutput == true then
     if UseTimeLeft == true then
       time = Calculate_Time(0)
       grp.update(AddressTimeLeft, time)
     end
     grp.write(AddressOutput, false)
   end
end
storage.delete('PID:' .. _SCRIPTNAME)
end
Reply
#51
This script must be event not resident.
Reply
#52
(27.05.2023, 07:06)admin Wrote: This script must be event not resident.

okay, what about the input address then? same as event address?


Okay I got It working, but not the way I think It should work!

If I put a 1 in the " AddressBlocking = '32/1/7' " then the script kinda works, but the retriggering is acting up, it jumps all over the place.

Maybe I expect the script to behave different,  I have 10 "toggle spring buttons" short press they send a 1 for a short time to 4/0/1 long press (2 sec) they send a 1 for a short time to 32/1/7, you click 1 button the light turns on, you click it again and the light keeps on but the timer resets to its beginning state. You hold 1 button for 2 sec and the light turns off and the timer goes to 0.
Code:
Event address '4/0/1'

-- Set input address
AddressInput = '4/0/1' -- 01. 1 bit (boolean)
-- Set output address
AddressOutput = '32/1/6' -- 01. 1 bit (boolean)
-- Set external time address (optional)
AddressExternalTime = '55/1/4' -- 07. 2 byte unsigned integer
-- Use time left indication
UseTimeLeft = true -- Set to false if no time left indication is used
-- Set feedback adress of time left indication (optional)
AddressTimeLeft = '32/1/5' -- 255 byte string
-- Set blocking address
AddressBlocking = '32/1/7'
-- Set time delay (Used when external time is not available)
SetDelay = 20 -- 2 minutes
-- Seconds or Minutes
SetSec = true -- Set to false for Minutes
-- Set factor delay (Multiplies Delay)
SetFac = 1
-- Logic can be turned of by value 0
Off_by_Value_Zero = false
Reply
#53
This script is meant for a single input control. It won't work correctly since you have separate on/off controls. What is the reason for such setup? It would make more sense to send 0 to the same group address when a long press is performed.
Reply
#54
(29.05.2023, 08:29)admin Wrote: This script is meant for a single input control. It won't work correctly since you have separate on/off controls. What is the reason for such setup? It would make more sense to send 0 to the same group address when a long press is performed.

I have 10 "toggle spring buttons" 
230v binary input knx device
Output relay knx device

The customer want to be able to turn ON the lights with a "short" press and start a timer (10 min).
If you press a "short" press when the lights are ON, the timer need to go back to the set value (10 min).
If you make a "long" press the lights need to turn OFF.
And they need to be able to see if the lights are ON or OFF and what time there is left until it turns OFF in the visualization.
Reply
#55
Here is my solution for a patio heater, controlled by wall switch but with a widget created in touch so the customer can set the timer as active or not and alter the timer length.

Code:
-- Event script attached to status object --

local state = event.getvalue()

now = os.time()

-- Group adress for time object, 2-byte unsigned
newtimer = grp.getvalue('1/1/1')
updatetimer = newtimer * 60
timer = updatetimer + now

-- Group adress for determining if timer is active or not, 1-bit
activeTimer = grp.getvalue('1/1/2')

if activeTimer == true then
    if state == true then
      storage.set('timerObject1', timer)
      script.enable('timerObject1(res)')
      log('Timer for timerObject1 is activated')
    else
      storage.set('timerObject1', 0)
      script.disable('timerObject1(res)')
    end
else
end


-------------- Script below is a resident script ----------------------------------
-- Resident script, set as inactive and 10 second execution time
-- Name this script the same as above (timerObject1(res))

time = os.time()

endTime = storage.get('timerObject1', 0)

if endTime == 0 then
 
else
    if time > endTime then
    -- Group adress for the SW-objekt
      grp.write('1/1/0', false)
    log('Timer for timerObject1 has expired, is has been turned off')
    end
end

Not as pretty as a simple script for all 10, but with more flexibility. Of course, all variable for storage and resident scripts need to have their named changed for this to work with more than 1 object.

Edit:

Here is what the widget looks like, and also attached. Please translate as needed.

   

Attached Files
.json   widget.json (Size: 1.36 KB / Downloads: 14)
Reply
#56
(29.05.2023, 09:04)FatMax Wrote: Here is my solution for a patio heater, controlled by wall switch but with a widget created in touch so the customer can set the timer as active or not and alter the timer length.

Code:
-- Event script attached to status object --

local state = event.getvalue()

now = os.time()

-- Group adress for time object, 2-byte unsigned
newtimer = grp.getvalue('1/1/1')
updatetimer = newtimer * 60
timer = updatetimer + now

-- Group adress for determining if timer is active or not, 1-bit
activeTimer = grp.getvalue('1/1/2')

if activeTimer == true then
    if state == true then
      storage.set('timerObject1', timer)
      script.enable('timerObject1(res)')
      log('Timer for timerObject1 is activated')
    else
      storage.set('timerObject1', 0)
      script.disable('timerObject1(res)')
    end
else
end


-------------- Script below is a resident script ----------------------------------
-- Resident script, set as inactive and 10 second execution time
-- Name this script the same as above (timerObject1(res))

time = os.time()

endTime = storage.get('timerObject1', 0)

if endTime == 0 then
 
else
    if time > endTime then
    -- Group adress for the SW-objekt
      grp.write('1/1/0', false)
    log('Timer for timerObject1 has expired, is has been turned off')
    end
end

Not as pretty as a simple script for all 10, but with more flexibility. Of course, all variable for storage and resident scripts need to have their named changed for this to work with more than 1 object.

Edit:

Here is what the widget looks like, and also attached. Please translate as needed.
Mange tak, behøver ikke oversætte det Smile
Reply
#57
Hi guys, I'm using Erwin's script.
But recently I've been getting this error message in the script log.
I use the script to operate an EnOcean switching module. It sends a short button signal to my garage door opener.
Everything still works, only this error message is new.
What could it be?

Thanks for the help
David

Code:
-- ** Staircase logic with external time object and retriggering on input object Version 3.1 ** --
-- ****************************** Created by Erwin van der Zwart **************************** --
-- ************************************** SET PARAMETERS ************************************ --
-- Set input address
AddressInput = '32/1/80'
-- Set output address
AddressOutput = '32/1/71'
-- Set unique name for staircase timer (to avoid same storage name usage)
StaircaseName = 'PIR_TR2_1'
-- Set external time address (optional)
AddressExternalTime = '4'
-- Use time left indication
UseTimeLeft = false -- Set to true if time left indication will be used
-- Set feedback adress of time left indication (optional)
AddressTimeLeft = ''
-- Set time delay (Used when external time is not available)
SetDelay = 2
-- Seconds or Minutes
SetSec = true-- Set to false for Minutes
-- Set factor delay (Multiplies Delay)
SetFac = 2
-- Logic can be turned of by value 0
Off_by_Value_Zero = false
-- ************************************** END PARAMETERS ************************************ --
-- *************************** DON'T CHANGE ANYTHING UNDER THIS LINE ************************ --
inputvalue = event.getvalue
if Off_by_Value_Zero == false and (event.getvalue() == false or event.getvalue() == 0) then
  -- Exit script
  return
end
ValueInput = grp.getvalue(AddressInput)
ValueOutput = grp.getvalue(AddressOutput)
ValueExternalTime = grp.getvalue(AddressExternalTime)
if SetSec == true then
  SetSeconds = 1
else
  SetSeconds = 60
end
if ValueExternalTime == nil then
ValueExternalTime = 0
end
if ValueExternalTime > 0 then
  StairCaseTime = ValueExternalTime * SetSeconds * SetFac
else
  StairCaseTime = SetDelay * SetSeconds * SetFac
end
if ValueInput == true then
  --check for earlier started scrips
  --check storage for stpid value
  stpid = storage.get(StaircaseName) 
  --check if stpid has a value
  if stpid == nil then
    pid = os.getpid()
    storage.set(StaircaseName, pid)
  else
    -- kill earlier running script   
    os.kill(stpid, signal.SIGKILL)
    -- create new pid for next time to kill
    pid = os.getpid() 
    storage.set(StaircaseName, pid)
  end
  if ValueOutput == false then
    grp.write(AddressOutput, true)
    ValueOutput = true
  end
  -- Check time left indication is used
  if UseTimeLeft == true then
    if StairCaseTime > 0 then
        grp.update(AddressTimeLeft, StairCaseTime)
        repeat
          StairCaseTime = StairCaseTime - 1
          grp.update(AddressTimeLeft, StairCaseTime)
          os.sleep(1)
        until StairCaseTime == 0
    end   
  else
    os.sleep(StairCaseTime)
  end
  ValueOutput = grp.getvalue(AddressOutput)
  if ValueOutput == true then
    ValueInput = grp.getvalue(AddressInput)
    if ValueInput == true then
        grp.write(AddressInput, false)
      ValueInput = false
    end
    if Off_by_Value_Zero == false then
      if ValueOutput == true then
            grp.write(AddressOutput, false)
        ValueOutput = false
          end
    else
      -- Do nothing, this will trigger else condition below on next run
    end
  end 
else
  --check for earlier started scrips
  --check storage for stpid value
  stpid = storage.get(StaircaseName) 
  --check if stpid has a value
  if stpid == nil then
  else
    -- kill earlier running script   
    os.kill(stpid, signal.SIGKILL)
    grp.update(AddressTimeLeft, 0)
    pid = nil
    storage.set(StaircaseName, pid)
  end
  if ValueOutput == true then
    grp.write(AddressOutput, false)
  end
end

Attached Files Thumbnail(s)
   
Reply
#58
Disable General configuration > Block unsafe functions in scripts.
Reply
#59
(11.09.2024, 13:40)admin Wrote: Disable General configuration > Block unsafe functions in scripts.

perfect, thank you for the info.
it works Big Grin Big Grin Big Grin
Reply


Forum Jump: