Presence Simulation (Record and Play Daily or weekly activity in KNX bus)
Example: Presence Simulation (Record and Play Daily or weekly activity in KNX bus)
Task
Sometimes it is necessary to record typical day or typical week and play it thus performing the presence simulation. This is useful for daily routine as well as for longer vacation period when home owners are leaving the house.
Create Recorder script
Add an Event-based script with TAG “Simulation” and paste the following Recorder script inside:
- -- Presence Simulation Module Version 2.0 - Recording part
- -- Created by Erwin van der Zwart
-
- -- ********** SET PARAMETERS ***********
-
- -- Set address for selecting modus of Simulation Module (0 = Recorder off, 1 = Record Today, 2 = Record Sunday, 3 = Record Monday, 4 = Record Tuesday, 5 = Record Wednesday, 6 = Record Thursday, 7 = Record Friday , 8 = Record Saturday, 9 = Record Midweek, 10 = Record Weekend, 11 = Record Full Week from Activation, 12 = Record Full Week from Day 1, 13 = Record Always, 14 = Record to Play Direct, 15 = Player Active, 16 = Delete all recorded data)
- Address_Modus = '1/1/125' -- Byte object ! -- Set as byte address with custom text and min/max 0 to 14 and make a separate button for value 15 with pincode (delete all recordings)
-
- -- Set address for play and stop
- Address_Play_Stop = '1/1/126' -- Bit object !
-
- -- Set address for delete recordings
- Address_Delete = '1/1/127' -- Bit object !
-
- -- Set Conformation LED play / stop (Optional)
- Address_Play_Stop_Feedback = '1/1/128' -- Bit object !
-
- -- Set Conformation LED delete (Optional)
- Address_Delete_Feedback = '1/1/129' -- Bit object !
-
- -- Set TAG used for selecting objects to be record
- Recording_Tag = 'Simulatie'
-
- -- Set scriptname for playing recorded objects and to disable/exit this recording (to avoid loop recording)
- Player_Script_Name = 'Simulatie Speler'
-
- -- Set name for storage for objects to be recorded
- Storage_Name = 'STSIMULATION'
-
- -- ********** END PARAMETERS ***********
-
- -- Check if player is active
- Current_Start_Stop_Value = grp.getvalue(Address_Play_Stop)
-
- if Current_Start_Stop_Value == true then
-
- -- Turn on feedback LED
- -- Get start / stop feedback value
- A_P_S = grp.getvalue(Address_Play_Stop_Feedback)
- if A_P_S == false then
- -- Enable start / stop feedback LED
- grp.update(Address_Play_Stop_Feedback, true)
- end
-
- -- Check if command is from modus selection
- -- Action on Selecting Modus
- if event.dst == Address_Modus then
- -- Get current script status
- Script_Status = script.status(Player_Script_Name)
- if Script_Status == true then
- -- Disable player script
- script.disable(Player_Script_Name)
- end
- -- Turn off feedback LED
- -- Get start / stop feedback value
- A_P_S = grp.getvalue(Address_Play_Stop_Feedback)
- if A_P_S == true then
- -- Disable start / stop feedback LED
- grp.update(Address_Play_Stop_Feedback, false)
- end
- -- Exit Script
- return
- end
-
- -- Get Object information
- objectinfo = grp.find(event.dst)
-
- -- Get simulation module modus value
- modus = grp.getvalue(Address_Modus)
-
- -- Check if modus value is between allowed range
- if modus >= 1 and modus <= 16 then
- -- Do nothing, move on with scipt
- else
- -- Turn off feedback LED
- -- Get start / stop feedback value
- A_P_S = grp.getvalue(Address_Play_Stop_Feedback)
- if A_P_S == true then
- -- Disable start / stop feedback LED
- grp.update(Address_Play_Stop_Feedback, false)
- end
- -- Exit script (also when modus = 0 for recorder off)
- return
- end
-
- -- Set Player Active
- if modus == 15 then
- -- Get status of player script
- status = script.status(Player_Script_Name)
- if status == false then
- -- Enable player script
- script.enable(Player_Script_Name)
- end
- -- Exit script
- return
- end
-
- -- Delete all recorded data
- if modus == 16 then
- -- Delete all recorded data from storage
- storage.set(Storage_Name, nil)
- alert('All recorded simulation data is deleted')
- -- Turn off feedback LED
- -- Get start / stop feedback value
- A_P_S = grp.getvalue(Address_Play_Stop_Feedback)
- if A_P_S == true then
- -- Disable start / stop feedback LED
- grp.update(Address_Play_Stop_Feedback, false)
- end
- -- Exit script
- return
- end
-
- -- Make sure events from Address_Modus are not recorded
- if event.dst == Address_Modus then
- -- Turn off feedback LED
- -- Get start / stop feedback value
- A_P_S = grp.getvalue(Address_Play_Stop_Feedback)
- if A_P_S == true then
- -- Disable start / stop feedback LED
- grp.update(Address_Play_Stop_Feedback, false)
- end
- -- Exit script
- return
- end
-
- -- Make sure events are not recorded while recorder is inactive, player is active or deletion mode is selected
- if modus == 0 or modus == 15 or modus == 16 then
- -- Exit script
- return
- end
-
- -- When modus is 1 to 14 then this will be executed:
-
- -- Get object last change timestamp to determine recording day
- objecttimestamp = objectinfo.updatetime
- objecttimetable = os.date("*t", objecttimestamp)
-
- -- Get Modus last change timestamp to determine recording day
- modusinfo = grp.find(Address_Modus)
- modustimestamp = modusinfo.updatetime
- modustimetable = os.date("*t", modustimestamp)
-
- if modus == 1 then -- Record Today
- if objecttimetable.yday == modustimetable.yday and objecttimetable.year == modustimetable.year then
- -- Continue Script
- else
- -- Exit Script
- return
- end
- elseif modus == 2 then -- Record Sonday
- if objecttimetable.wday == 1 and (objecttimestamp - modustimestamp) < 604800 then
- -- Continue Script
- else
- -- Exit Script
- return
- end
- elseif modus == 3 then -- Record Monday
- if objecttimetable.wday == 2 and (objecttimestamp - modustimestamp) < 604800 then
- -- Continue Script
- else
- -- Exit Script
- return
- end
- elseif modus == 4 then -- Record Tuesday
- if objecttimetable.wday == 3 and (objecttimestamp - modustimestamp) < 604800 then
- -- Continue Script
- else
- -- Exit Script
- return
- end
- elseif modus == 5 then -- Record Wednesday
- if objecttimetable.wday == 4 and (objecttimestamp - modustimestamp) < 604800 then
- -- Continue Script
- else
- -- Exit Script
- return
- end
- elseif modus == 6 then -- Record Thursday
- if objecttimetable.wday == 5 and (objecttimestamp - modustimestamp) < 604800 then
- -- Continue Script
- else
- -- Exit Script
- return
- end
- elseif modus == 7 then -- Record Friday
- if objecttimetable.wday == 6 and (objecttimestamp - modustimestamp) < 604800 then
- -- Continue Script
- else
- -- Exit Script
- return
- end
- elseif modus == 8 then -- Record Saturday
- if objecttimetable.wday == 7 and (objecttimestamp - modustimestamp) < 604800 then
- -- Continue Script
- else
- -- Exit Script
- return
- end
- elseif modus == 9 then -- Record Midweek
- if objecttimetable.wday == 2 or objecttimetable.wday == 3 or objecttimetable.wday == 4 or objecttimetable.wday == 5 or objecttimetable.wday == 6 and (objecttimestamp - modustimestamp) < 604800 then
- -- Continue Script
- else
- -- Exit Script
- return
- end
- elseif modus == 10 then -- Record Weekend
- if objecttimetable.wday == 1 or objecttimetable.wday == 7 and (objecttimestamp - modustimestamp) < 604800 then
- -- Continue Script
- else
- -- Exit Script
- return
- end
- elseif modus == 11 then -- Record Full Week from Activation
- if (objecttimestamp - modustimestamp) < 604800 then
- -- Continue Script
- else
- -- Exit Script
- return
- end
- elseif modus == 12 then -- Record Full Week from Day 1
- if ((modustimetable.yday - modustimetable.wday) + 7) == (updatetimetable.yday - updatetimetable.wday) and (objecttimestamp - modustimestamp) < 604800 then
- -- Continue Script
- else
- -- Exit Script
- return
- end
- elseif modus == 13 or modus == 14 then -- Record Alway
- -- Continue Script
- end
-
- -- Get previous data from storage if available
- Storage_Data = storage.get(Storage_Name)
- -- Check if storagedata is available
- if Storage_Data == nil then
- -- Create new data table
- Storage_Data = {}
- table.insert(Storage_Data, {updatetime = objectinfo.updatetime, address = objectinfo.address, value = objectinfo.value, recordmodus = modus, lastplayed = os.time(), timelap = (objectinfo.updatetime - modusinfo.updatetime)})
- else
- -- Loop through array and clear to all found items older then selected scope or from other modus in the recorded values
- for index, StorageData in pairs(Storage_Data) do
- -- Check value before write (if enabled)
- if StorageData.recordtime == nil then
- -- Do nothing
- else
- if (os.time() - StorageData.recordtime) > 604800 or StorageData.recordmodus ~= modus then
- -- Delete item
- Storage_Data[index]=nil
- end
- end
- end
- -- Add data to excisting data table
- table.insert(Storage_Data, {updatetime = objectinfo.updatetime, address = objectinfo.address, value = objectinfo.value, recordmodus = modus, lastplayed = os.time(), timelap = (objectinfo.updatetime - Storage_Data[#Storage_Data].updatetime)})
- end
-
- -- Write new data back to storage
- storage.set(Storage_Name, Storage_Data)
- log (Storage_Data)
- else
-
- -- Get current script status
- Script_Status = script.status(Player_Script_Name)
- if Script_Status == true then
- -- Disable player script
- script.disable(Player_Script_Name)
- end
-
- -- Turn off feedback LED
- -- Get start / stop feedback value
- A_P_S = grp.getvalue(Address_Play_Stop_Feedback)
- if A_P_S == true then
- -- Disable start / stop feedback LED
- grp.update(Address_Play_Stop_Feedback, false)
- end
-
- end
Create Player script
Add a Resident script and set it to 1 second and paste the player script inside. Make sure that the script name of this resident script mach with the line 25 (Player_Script_Name) of the player script and disable this script.
The script will be activated/deactivated automatically by the event based script when needed.
- -- Simulation Module Version 2.0 - Player Part
- -- Created by Erwin van der Zwart
-
- -- ********** SET PARAMETERS ***********
-
- -- Set storage name for recorded objects
- Storage_Name = 'STSIMULATION'
-
- -- Check object if object value is changed before write (same value = no write)
- Check_Object_Changed = false
-
- -- Set random value in seconds to make switching time not exactly the same time as recording time so you won't see its automaticly send every day the same time.
- Sec_Random = 1800 -- makes bandwidth of 30 minutes when recording will be played, set to 0 will disable the random function and objects will be played on same time as recording.
-
- -- ********** END PARAMETERS ***********
-
- -- Get data from storage if available
- Storage_Data = storage.get(Storage_Name)
-
- -- Set Data table changed to false (save data only when changes are detected)
- Data_Table_Changed = false
-
- -- Check if storagedata is available
- if Storage_Data == nil then
- -- Exit Script
- return
- else
- -- Sort table by updatetime
- table.sort(Storage_Data, function(first,last) return first.updatetime < last.updatetime end)
-
- -- Set current time as timestamp for calculation
- timestamp = os.time()
-
- -- Set counter to 0
- counter = 0
-
- -- Loop through array and write to all found addresses in this recording the values
- for index, StorageData in ipairs(Storage_Data) do
- ::ObjectStart::
-
- -- Create random value to play object not exactly on same time but in a scope of 30 minutes from that time.
- random = math.random(0, Sec_Random)
-
- -- Create table from current time to get wday and yday
- currenttimetable = os.date("*t", os.time())
-
- -- Create table from lastplayed to get wday and yday
- lastplayedtable = os.date("*t", StorageData.lastplayed)
-
- -- Check if recordmodus is available
- if StorageData.recordmodus == nil then
- -- Do nothing, move to next object because this object has no modus info.
- counter = counter + StorageData.timelap
- goto NextObject
- else
- modus = StorageData.recordmodus
- end
-
- -- Check if weekday matches with recording (not for modus 1 and 14)
- if modus ~= 1 or modus ~= 14 then
- if currenttimetable.wday == lastplayedtable.wday then
- -- Day matches, move on with script
- else
- -- Do nothing, move to next object because this object doesn't mach day of playing.
- counter = counter + StorageData.timelap
- goto NextObject
- end
- end
-
- -- Check value before write (if enabled)
- if Check_Object_Changed == true then
- -- Get current object value
- value = grp.getvalue(StorageData.address)
- -- Check if value is the same
- if value == StorageData.value then
- -- Do nothing, move to next object because this object aready is set to wanted value.
- counter = counter + StorageData.timelap
- goto NextObject
- end
- end
-
- -- Selected actions by modus
- if modus == 14 then -- Play Direct (for testing your recordings)
- -- Check if this is first object from table
- if index == 1 then
- if (timestamp + StorageData.timelap) <= os.time() then
- -- Move to write to object part
- counter = counter + StorageData.timelap
- else
- -- Slow looping down when object does not meet requirements (only for modus 14)
- os.sleep (1)
- -- Try again
- goto ObjectStart
- end
- else
- if ((timestamp + StorageData.timelap) + counter) <= os.time() then
- -- Move to write to object part
- counter = counter + StorageData.timelap
- else
- -- Slow looping down when object does not meet requirements (only for modus 14)
- os.sleep (1)
- -- Try again
- goto ObjectStart
- end
- end
- end
-
- -- ** Write to object part ** (all previous conditions are true)
-
- -- Write function for modus 1 to 13 to use when conditions are true
- function WritetoObject_Modus_1_to_13()
- -- Calculate if object is at least 12 hrs old (to make sure it won't be played again the same day)
- if os.time() - StorageData.lastplayed >= 43200 then
- timedifference = os.time() - StorageData.updatetime
- -- Perform modulo 86400 to clear all passed days from time difference and keep time difference < 24hrs
- result = timedifference % 86400
- -- Calculate seconds to complete minutes past
- result = math.floor(result / 60)
- -- Calculate random to complete minutes to calculate
- random = math.floor(random / 60)
- -- Create a random time between 30 minutes to meet condition to play
- if result >= (1440 - random) then -- 1440 = 60 min * 24 hrs
- -- Write address to wanted value
- grp.write(StorageData.address, StorageData.value)
- -- Update timestamp last played
- StorageData.lastplayed = os.time()
- Data_Table_Changed = true
- end
- end
- end
-
- -- Write function for modus 14 to use when conditions are true
- function WritetoObject_Modus_14()
- grp.write(StorageData.address, StorageData.value)
- end
-
- -- Determine what objects to play on recording conditions
- if modus == 1 then -- Today
- WritetoObject_Modus_1_to_13()
- elseif modus == 2 and currenttimetable.wday == 1 then -- Sunday
- WritetoObject_Modus_1_to_13()
- elseif modus == 3 and currenttimetable.wday == 2 then -- Monday
- WritetoObject_Modus_1_to_13()
- elseif modus == 4 and currenttimetable.wday == 3 then -- Tuesday
- WritetoObject_Modus_1_to_13()
- elseif modus == 5 and currenttimetable.wday == 4 then -- Wednesday
- WritetoObject_Modus_1_to_13()
- elseif modus == 6 and currenttimetable.wday == 5 then -- Thursday
- WritetoObject_Modus_1_to_13()
- elseif modus == 7 and currenttimetable.wday == 6 then -- Vriday
- WritetoObject_Modus_1_to_13()
- elseif modus == 8 and currenttimetable.wday == 7 then -- Saterday
- WritetoObject_Modus_1_to_13()
- elseif modus == 9 and (currenttimetable.wday == 2 or currenttimetable.wday == 3 or currenttimetable.wday == 4 or currenttimetable.wday == 5 or currenttimetable.wday == 6) then -- Midweek
- WritetoObject_Modus_1_to_13()
- elseif modus == 10 and (currenttimetable.wday == 1 or currenttimetable.wday == 7) then -- Weekend
- WritetoObject_Modus_1_to_13()
- elseif (modus == 11 or modus == 12 or modus == 13) and currenttimetable.wday == lastplayedtable.wday then -- Full Week from Activation, Full Week from Day 1 and Always
- WritetoObject_Modus_1_to_13()
- elseif modus == 14 then -- Play direct
- WritetoObject_Modus_14()
- end
- ::NextObject::
- end
- -- Write changed table back to storage when timestamp lastplayed is changed during playing
- if Data_Table_Changed == true then
- -- Write new data back to storage
- storage.set(Storage_Name, Storage_Data)
- end
- end
Create a byte object (05. 1 byte unsigned integer) and add TAG “Simulation” to it
Create at least 4 buttons that sends a fixed value to the byte address (created on step 3) for each one of the next options you want to use:
- 0 = Recorder off (This one you will need most of the time)
- 1 = Record Today
- 2 = Record Sunday
- 3 = Record Monday
- 4 = Record Tuesday
- 5 = Record Wednesday
- 6 = Record Thursday
- 7 = Record Friday
- 8 = Record Saturday
- 9 = Record Midweek
- 10 = Record Weekend
- 11 = Record Full Week from Activation
- 12 = Record Full Week from Day 1
- 13 = Record Always (This one you will need most of the time, if customer want something else you can use 1 to 12)
- 14 = Record to Play Direct (This one is for testing only without having to wait a full day, it plays direct your recordings)
- 15 = Player Active (This one you will need always to set player active with previous recorded values)
- 16 = Delete all recorded data (This one you will need if you want to be able to delete your recordings)
Add TAGS “Simulation” to all objects you want to record with recorder
Notes:
- Run with value 13 on byte object to start recording, record for a week (or longer -> first in – last out if older then a week) when activating after a week the recorder will stop recording and play all telegrammes based on day and time
- You can set Sec_Random in the event based player script to make a random difference between the seconds set (1800 = half hour) to make the script add randomness in the playing within that scope.
So telegram recorded last saterday 15:05 may be played random (once) between 14:50 and 15:20 so you won’t be able to tell its automatic recorded playing. Set to 0 seconds to play on exact time. - You can set Check_Object_Changed to true if you don’t want to send value if the controller has already this value to lower bus load.
Created by Erwin van der Zwart