20.04.2022, 08:48 (This post was last modified: 25.04.2022, 08:09 by davidchispas.)
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
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
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
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.
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!
(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
27.05.2023, 07:18 (This post was last modified: 27.05.2023, 07:39 by Novodk.)
(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
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.
(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.
29.05.2023, 09:04 (This post was last modified: 01.06.2023, 11:54 by FatMax.)
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.
(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.
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