Posts: 15
Threads: 4
Joined: Nov 2019
Reputation:
0
I use the Staircase script frequently. Thanks again!
Now I would like to use it also in rooms where by default a dimmed lamp burns softly (e.g. 30%), for example in the hallway. When the motion detector of the alarm detects someone in the hallway, I would like the lamp to burn brighter for a defined time (e.g. 90% for 60 seconds). And then back to the old dimming value (30%).
If the lamp is turned off in the meantime, it should go out and turn on again the next time in the dimming mode (30%). I've tried all kinds of things, but can't get it to work properly.
Every time it seems to work, but then there is still an error somewhere. And I have solved it with three different scripts. It is complex to explain. Can someone please modify the script in such a way that this does work properly? Hopefully more people will enjoy this enhanced script  .
Thanks in advance!
Posts: 5200
Threads: 29
Joined: Jun 2015
Reputation:
266
Can you post the scripts that you are using now?
Posts: 15
Threads: 4
Joined: Nov 2019
Reputation:
0
19.05.2022, 18:52
(This post was last modified: 19.05.2022, 18:57 by Dirk79.)
(16.03.2022, 09:25)admin Wrote: Can you post the scripts that you are using now?
Thanks for the question and apologies for not responding sooner. I did not notice your response.
I created two different scripts. The first one responds to the PIR. For that, I modified the beautiful Staircase logic. See my explanation in the script. I added a lot of logs to find for myself why it doesn't work properly.
With the second script I want to achieve that if the lamp is turned off (manually or by a program or by another script), that the running first script then stops, that the lamp goes off, and the next time it comes on again dimmed.
In practice, it works well, until we go to bed. Usually the lamp goes off first and then on again. Also, regularly the lamp goes directly on at the brighter setting. So then the brighter setting is the same as the standard setting.
It would be best if this could work with just 1 script. I would like to use this script in more places in the house (and especially outside).
Here are the scripts:
SCRIPT 1:
Code: -- Note that at 3/0/7 there is also a script, which should stop this staircase if the lamp is turned on/off.
-- Only react to PIR going on, not going off:
inputvalue = event.getvalue
if (event.getvalue() == false or event.getvalue() == 0) then
return
end
-- ** 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 = '9/0/1'
-- Set output address
AddressOutput = '1/1/7'
-- Set unique name for staircase timer (to avoid same storage name usage)
StaircaseName = 'PIR_LAMP_HAL'
-- Set external time address (optional)
AddressExternalTime = ''
-- 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 = 15
-- 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
-- Additions for brightening the lamp on motion dd 14-01-2022:
Schemer = grp.getvalue('6/0/6') -- If it is dusk, this is set to 1.
AanStandHal = grp.getvalue('3/0/7') -- This indicates whether lamp is on when the script starts.
DimStandHal = grp.getvalue('3/2/7') -- This is the dimming value at the start of the script.
AdressOpgeslagenBeginStandHal = '1/2/7'-- Dim value field especially for this script to save initial state (I don't manage to keep this in memory)
OpgeslagenBeginStandHal = grp.getvalue(AdressOpgeslagenBeginStandHal)
TijdelijkHarderHal = grp.getvalue('39/1/1') -- This contains the (brighter) dimming value that the lamp should temporarily be at (e.g. 90%)
-- If DimStandHal is set to 0, the lamp is off, and nothing needs to happen. Nothing needs to happen when there is no twilight either.
if (DimStandHal) == 0 or (Schemer) == false then
--log('return because hall lamp not on or no dusk')
return
end
-- If DimStandHal == TemporaryHarderHal, then the script has run before. Then nothing should happen. Otherwise save current value.
if DimStandHal == TijdelijkHarderHal then
--log ('DimStandHal gelijk aan TijdelijkeHarderHal')
--log ('OpgeslagenBeginStandHal is'..OpgeslagenBeginStandHal)
--log ('TijdelijkHarderHal is'..TijdelijkHarderHal)
-- do nothing
else
grp.write(AdressOpgeslagenBeginStandHal, DimStandHal)
OpgeslagenBeginStandHal = grp.getvalue(AdressOpgeslagenBeginStandHal)
--log ('Nieuwe start script (Else)')
--log ('OpgeslagenBeginStandHal is'..OpgeslagenBeginStandHal)
--log ('DimStandHal is'..DimStandHal)
--log ('TijdelijkHarderHal is'..TijdelijkHarderHal)
end
-- ************************************** 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 < TijdelijkHarderHal then
grp.write(AddressOutput, TijdelijkHarderHal)
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 == TijdelijkHarderHal 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 == TijdelijkHarderHal then
grp.write(AddressOutput, OpgeslagenBeginStandHal) -- value here was 0, but to OpgeslagenBeginStandHal to set it back to this position.
ValueOutput = 0
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
-- Terugschrijven naar beginstand
if ValueOutput == TijdelijkHarderHal then
--log ('ValueOutput == TijdelijkHarderHal')
grp.write(AddressOutput, OpgeslagenBeginStandHal)
end
end
SCRIPT 2:
Code: --When switching this lamp, any running script on the hall PIR (9/0/1) is stopped.
--This script is activacted at 3/0/7 and 0/1/1 (group lighting downstairs)
StaircaseName = 'PIR_LAMP_HAL'
stpid = storage.get(StaircaseName)
os.kill(stpid, signal.SIGKILL)
grp.update(AddressTimeLeft, 0)
pid = nil
storage.set(StaircaseName, pid)
-- If script is turned off, while the lamp is louder during the PIR script, then the "old" DimValue must be written over the feedback.
-- The idea is that the lamp then starts up the next time with the harder setting (on which it went out) and then automatically dims back to the old dimming value.
OpgeslagenDimWaarde = grp.getvalue('1/2/7')
grp.write('3/2/7', OpgeslagenDimWaarde)
Posts: 5200
Threads: 29
Joined: Jun 2015
Reputation:
266
This is easier to do via a resident script (0 sleep time).
Single script can be used for multiple timers by adding more entries to the timers table. The script uses current dimmer control value to determine what level to set, only values set by other sources (not the script itself) are used. There are two points - high and low, if the current value is less than low then low value is used, otherwise high value is used. The status value cannot be used here because it's impossible to tell who change the value that caused the status update.
An alternative solution is to create a day/night object to have two different sets of values for movement/no movement.
Code: if not client then
timers = {
{
input = '1/1/1', -- binary PIR status
output = '1/1/3', -- dimmer control (0..100%)
onvaluehigh = 90, -- high output value in %
onvaluelow = 30, -- low output value in %
timeout = 60, -- in seconds
}
}
function setinputvalue(timer, event)
local value = busdatatype.decode(event.datahex, dt.bool)
if not value then
return
end
if timer.outvalue < timer.onvaluelow then
value = timer.onvaluelow
elseif timer.outvalue < timer.onvaluehigh then
value = timer.onvaluehigh
else
value = nil
end
if not timer.ticks and value then
grp.write(timer.output, value, dt.scale)
end
timer.ticks = timer.timeout
end
function setoutputvalue(timer, event)
timer.outvalue = busdatatype.decode(event.datahex, dt.scale)
timer.ticks = nil -- stop timer
end
for _, timer in ipairs(timers) do
timer.outvalue = grp.getvalue(timer.output)
end
sender = 'tm'
grp.sender = sender
client = require('localbus').new(0.1)
client:sethandler('groupwrite', function(event)
if event.sender == sender then
return
end
for _, timer in ipairs(timers) do
if timer.input == event.dst then
setinputvalue(timer, event)
elseif timer.output == event.dst then
setoutputvalue(timer, event)
end
end
end)
end
client:loop(1)
for _, timer in ipairs(timers) do
if timer.ticks then
timer.ticks = timer.ticks - 1
if timer.ticks == 0 then
grp.write(timer.output, timer.outvalue, dt.scale)
timer.ticks = nil
end
end
end
Posts: 15
Threads: 4
Joined: Nov 2019
Reputation:
0
This script is more complex than I can make myself. Very cool to see that you made this one so for me. Thank you, thank you!
I have adopted the script as resident script with 0 sleep time.
I also deleted my own scripts.
I filled in the table as follows:
{
input = '9/0/1', -- binary PIR status
output = '1/1/7', -- dimmer control (0..100%)
onvaluehigh = 90, -- high output value in %
onvaluelow = 10, -- low output value in %
timeout = 5, -- in seconds
}
And I also tried filling in the status object 3/1/7 as output instead of 1/1/7.
After two weeks, I tried one more time of all. Sometimes you then suddenly see what is going wrong. Unfortunately, I am not able to get this script to work.
Can you give any advice...?
Posts: 5200
Threads: 29
Joined: Jun 2015
Reputation:
266
Post your full script listing. Also enable logging for mapped object and see if they change values. Check Error logs as well.
Posts: 15
Threads: 4
Joined: Nov 2019
Reputation:
0
(09.06.2022, 15:33)admin Wrote: Post your full script listing. Also enable logging for mapped object and see if they change values. Check Error logs as well.
Thanks. Here is my answer:
Sorry for not noticing the error log. There are errors:
---
Resident script:19: attempt to compare nil with number
stack traceback:
Resident script:19: in function 'setinputvalue'
Resident script:54: in function <Resident script:47>
Library localbus: in function ''
Library localbus: in function ''
Library localbus: in function 'loop'
---
'1/1/7' is the object of the dimming value.
I have logged 1/1/7. No logs registered.
Code: if not client then
timers = {
{
input = '9/0/1', -- binary PIR status
output = '1/1/7', -- dimmer control (0..100%)
onvaluehigh = 90, -- high output value in %
onvaluelow = 10, -- low output value in %
timeout = 5, -- in seconds
}
}
function setinputvalue(timer, event)
local value = busdatatype.decode(event.datahex, dt.bool)
if not value then
return
end
if timer.value < timer.onvaluelow then
value = timer.onvaluelow
elseif timer.outvalue < timer.onvaluehigh then
value = timer.onvaluehigh
else
value = nil
end
if not timer.ticks and value then
grp.write(timer.output, value, dt.scale)
end
timer.ticks = timer.timeout
end
function setoutputvalue(timer, event)
timer.outvalue = busdatatype.decode(event.datahex, dt.scale)
timer.ticks = nil -- stop timer
end
for _, timer in ipairs(timers) do
timer.outvalue = grp.getvalue(timer.output)
end
sender = 'tm'
grp.sender = sender
client = require('localbus').new(0.1)
client:sethandler('groupwrite', function(event)
if event.sender == sender then
return
end
for _, timer in ipairs(timers) do
if timer.input == event.dst then
setinputvalue(timer, event)
elseif timer.output == event.dst then
setoutputvalue(timer, event)
end
end
end)
end
client:loop(1)
for _, timer in ipairs(timers) do
if timer.ticks then
timer.ticks = timer.ticks - 1
if timer.ticks == 0 then
grp.write(timer.output, timer.outvalue, dt.scale)
timer.ticks = nil
end
end
end
Posts: 5200
Threads: 29
Joined: Jun 2015
Reputation:
266
Line 19 should have timer.outvalue instead of timer.value:
Code: if timer.outvalue < timer.onvaluelow then
Posts: 15
Threads: 4
Joined: Nov 2019
Reputation:
0
(13.06.2022, 07:00)admin Wrote: Line 19 should have timer.outvalue instead of timer.value:
Code: if timer.outvalue < timer.onvaluelow then
Thanks for reaction. I have changed this, but error is the same:
Code: if not client then
timers = {
{
input = '9/0/1', -- binary PIR status
output = '1/1/7', -- dimmer control (0..100%)
onvaluehigh = 90, -- high output value in %
onvaluelow = 10, -- low output value in %
timeout = 5, -- in seconds
}
}
function setinputvalue(timer, event)
local value = busdatatype.decode(event.datahex, dt.bool)
if not value then
return
end
if timer.outvalue < timer.onvaluelow then
value = timer.onvaluelow
elseif timer.outvalue < timer.onvaluehigh then
value = timer.onvaluehigh
else
value = nil
end
if not timer.ticks and value then
grp.write(timer.output, value, dt.scale)
end
timer.ticks = timer.timeout
end
function setoutputvalue(timer, event)
timer.outvalue = busdatatype.decode(event.datahex, dt.scale)
timer.ticks = nil -- stop timer
end
for _, timer in ipairs(timers) do
timer.outvalue = grp.getvalue(timer.output)
end
sender = 'tm'
grp.sender = sender
client = require('localbus').new(0.1)
client:sethandler('groupwrite', function(event)
if event.sender == sender then
return
end
for _, timer in ipairs(timers) do
if timer.input == event.dst then
setinputvalue(timer, event)
elseif timer.output == event.dst then
setoutputvalue(timer, event)
end
end
end)
end
client:loop(1)
for _, timer in ipairs(timers) do
if timer.ticks then
timer.ticks = timer.ticks - 1
if timer.ticks == 0 then
grp.write(timer.output, timer.outvalue, dt.scale)
timer.ticks = nil
end
end
end
Resident script:19: attempt to compare nil with number
stack traceback:
Resident script:19: in function 'setinputvalue'
Resident script:54: in function <Resident script:47>
Library localbus: in function ''
Library localbus: in function ''
Lib brarary localbus: in function 'loop'
Posts: 5200
Threads: 29
Joined: Jun 2015
Reputation:
266
The same script works for me. Does 9/0/1 exist?
I've added some logging, check what you get in the Logs tab:
Code: if not client then
timers = {
{
input = '9/0/1', -- binary PIR status
output = '1/1/7', -- dimmer control (0..100%)
onvaluehigh = 90, -- high output value in %
onvaluelow = 10, -- low output value in %
timeout = 5, -- in seconds
}
}
function setinputvalue(timer, event)
local value = busdatatype.decode(event.datahex, dt.bool)
if not value then
return
end
log('pir trigger', timer.outvalue)
if timer.outvalue < timer.onvaluelow then
value = timer.onvaluelow
elseif timer.outvalue < timer.onvaluehigh then
value = timer.onvaluehigh
else
value = nil
end
if not timer.ticks and value then
grp.write(timer.output, value, dt.scale)
end
timer.ticks = timer.timeout
end
function setoutputvalue(timer, event)
timer.outvalue = busdatatype.decode(event.datahex, dt.scale)
timer.ticks = nil -- stop timer
log('output change', timer.outvalue)
end
for _, timer in ipairs(timers) do
timer.outvalue = grp.getvalue(timer.output)
log('timer init', timer.outvalue)
end
sender = 'tm'
grp.sender = sender
client = require('localbus').new(0.1)
client:sethandler('groupwrite', function(event)
if event.sender == sender then
return
end
for _, timer in ipairs(timers) do
if timer.input == event.dst then
setinputvalue(timer, event)
elseif timer.output == event.dst then
setoutputvalue(timer, event)
end
end
end)
end
client:loop(1)
for _, timer in ipairs(timers) do
if timer.ticks then
timer.ticks = timer.ticks - 1
if timer.ticks == 0 then
log('timer stop', timer.outvalue)
grp.write(timer.output, timer.outvalue, dt.scale)
timer.ticks = nil
end
end
end
Posts: 15
Threads: 4
Joined: Nov 2019
Reputation:
0
I modified the code and it still did not work. I searched for a while, until I found the ultimate solution: reboot the device. After that, the error messages were immediately gone, and the script works. I can't explain it, but fortunately it's solved. Thank you, thank you!
I'm still running into a problem, and I also have a question about applying this script to other lamps inside and outside the house:
Problem:
If the script is running and has turned the lamp up when there is movement, and then within the set time period the lamp is turned off (for example, because we are going to bed), then the script turns the lamp - after the set time has expired - back on to its original dimming status. Unfortunately, this was not the intention. The lamp should then remain off. Is there a solution for this?
Question:
Is it possible to indicate in the script for each PIR via a separate object whether it is active or not?
And is it possible to include a PIR twice in the timers table. For example, an outdoor lamp in the backyard in winter has a different behaviour than in summer?
Posts: 5200
Threads: 29
Joined: Jun 2015
Reputation:
266
A separate timer on/off object can be added as well as another on/off object to monitor manual lamp operation. What should happen when the lamp is turned on manually? Which level should the script use?
Posts: 15
Threads: 4
Joined: Nov 2019
Reputation:
0
17.06.2022, 17:01
(This post was last modified: 17.06.2022, 17:22 by Dirk79.)
(17.06.2022, 14:18)admin Wrote: A separate timer on/off object can be added as well as another on/off object to monitor manual lamp operation. What should happen when the lamp is turned on manually? Which level should the script use?
Super that this is possible!
Preferably, the lamp returns to its last initial position. That is, the position the lamp was in the last time, before movement was detected.
E.g. the lamp is on 35%, it is turned to 90% by the script and is then turned off manually or by another script or a scene. The next time the lamp is turned on manually (or by a scene), it should start at 35%.
But if that results in a very complex script, then a fixed value, e.g. 30%, is also good.
Thanks.
Posts: 5200
Threads: 29
Joined: Jun 2015
Reputation:
266
Updated version with an optional timer on/off object and status monitoring. For this to work the dimmer must have a status output active and should report the status as soon as possible (some dimmers have an option to report status when only after a transition). When the script writes its output value it will ignore the status report for 1 second. This can be lowered (line 53) to prevent possible issues when a manual control is performed at the same moment as the scripted control.
Code: if not client then
timers = {
{
input = '1/1/1', -- binary PIR status
enable = '1/1/2', -- enable timer on/off
output = '1/1/3', -- dimmer control (0..100%)
status = '1/1/4', -- dimmer status (0..100%)
onvaluehigh = 90, -- high output value in %
onvaluelow = 10, -- low output value in %
timeout = 5, -- in seconds
}
}
function setoutputvalue(timer, value)
timer.ignoresec, timer.ignoreusec = os.microtime()
grp.write(timer.output, value, dt.scale)
end
function settimerstate(timer, event)
timer.enabled = busdatatype.decode(event.datahex, dt.bool)
timer.ticks = nil
log('timer enabled', timer.enabled)
end
function setinputvalue(timer, event)
local value = busdatatype.decode(event.datahex, dt.bool)
if not value or not timer.enabled then
return
end
log('pir trigger', timer.statvalue)
if timer.statvalue < timer.onvaluelow then
value = timer.onvaluelow
elseif timer.statvalue < timer.onvaluehigh then
value = timer.onvaluehigh
else
value = nil
end
if not timer.ticks and value then
setoutputvalue(timer, value)
end
timer.ticks = timer.timeout
end
function setstatusvalue(timer, event)
if timer.ignoresec then
local delta = os.udifftime(timer.ignoresec, timer.ignoreusec)
if delta >= 0 and delta <= 1 then
return
end
timer.ignoresec = nil
end
if timer.enabled then
timer.statvalue = busdatatype.decode(event.datahex, dt.scale)
timer.ticks = nil -- stop timer
log('output change', timer.statvalue)
end
end
for _, timer in ipairs(timers) do
timer.statvalue = grp.getvalue(timer.status) or 0
if timer.enable then
timer.enabled = grp.getvalue(timer.enable)
else
timer.enabled = true
end
log('timer init', timer.statvalue, timer.enabled)
end
sender = 'tm'
grp.sender = sender
client = require('localbus').new(0.1)
client:sethandler('groupwrite', function(event)
if event.sender == sender then
return
end
for _, timer in ipairs(timers) do
if event.dst == timer.enable then
settimerstate(timer, event)
elseif event.dst == timer.switch then
setswitchvalue(timer, event)
elseif event.dst == timer.input then
setinputvalue(timer, event)
elseif event.dst == timer.status then
setstatusvalue(timer, event)
end
end
end)
end
client:loop(1)
for _, timer in ipairs(timers) do
if timer.ticks then
timer.ticks = timer.ticks - 1
if timer.ticks == 0 then
log('timer stop', timer.statvalue)
setoutputvalue(timer, timer.statvalue)
timer.ticks = nil
end
end
end
|