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:

Source code    
  1. -- Presence Simulation Module Version 2.0 - Recording part
  2. -- Created by Erwin van der Zwart
  3.  
  4. -- ********** SET PARAMETERS ***********
  5.  
  6. -- 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)
  7. 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)
  8.  
  9. -- Set address for play and stop
  10. Address_Play_Stop = '1/1/126' -- Bit object !
  11.  
  12. -- Set address for delete recordings
  13. Address_Delete = '1/1/127' -- Bit object !
  14.  
  15. -- Set Conformation LED play / stop (Optional)
  16. Address_Play_Stop_Feedback = '1/1/128' -- Bit object !
  17.  
  18. -- Set Conformation LED delete (Optional)
  19. Address_Delete_Feedback = '1/1/129' -- Bit object !
  20.  
  21. -- Set TAG used for selecting objects to be record
  22. Recording_Tag = 'Simulatie'
  23.  
  24. -- Set scriptname for playing recorded objects and to disable/exit this recording (to avoid loop recording)
  25. Player_Script_Name = 'Simulatie Speler'
  26.  
  27. -- Set name for storage for objects to be recorded
  28. Storage_Name = 'STSIMULATION'
  29.  
  30. -- ********** END PARAMETERS ***********
  31.  
  32. -- Check if player is active
  33. Current_Start_Stop_Value = grp.getvalue(Address_Play_Stop)
  34.  
  35. if Current_Start_Stop_Value == true then
  36.  
  37. -- Turn on feedback LED
  38. -- Get start / stop feedback value
  39. A_P_S = grp.getvalue(Address_Play_Stop_Feedback)
  40. if A_P_S == false then
  41. -- Enable start / stop feedback LED
  42. grp.update(Address_Play_Stop_Feedback, true)
  43. end
  44.  
  45. -- Check if command is from modus selection
  46. -- Action on Selecting Modus
  47. if event.dst == Address_Modus then
  48. -- Get current script status
  49. Script_Status = script.status(Player_Script_Name)
  50. if Script_Status == true then
  51. -- Disable player script
  52. script.disable(Player_Script_Name)
  53. end
  54. -- Turn off feedback LED
  55. -- Get start / stop feedback value
  56. A_P_S = grp.getvalue(Address_Play_Stop_Feedback)
  57. if A_P_S == true then
  58. -- Disable start / stop feedback LED
  59. grp.update(Address_Play_Stop_Feedback, false)
  60. end
  61. -- Exit Script
  62. return
  63. end
  64.  
  65. -- Get Object information
  66. objectinfo = grp.find(event.dst)
  67.  
  68. -- Get simulation module modus value
  69. modus = grp.getvalue(Address_Modus)
  70.  
  71. -- Check if modus value is between allowed range
  72. if modus >= 1 and modus <= 16 then
  73. -- Do nothing, move on with scipt
  74. else
  75. -- Turn off feedback LED
  76. -- Get start / stop feedback value
  77. A_P_S = grp.getvalue(Address_Play_Stop_Feedback)
  78. if A_P_S == true then
  79. -- Disable start / stop feedback LED
  80. grp.update(Address_Play_Stop_Feedback, false)
  81. end
  82. -- Exit script (also when modus = 0 for recorder off)
  83. return
  84. end
  85.  
  86. -- Set Player Active
  87. if modus == 15 then
  88. -- Get status of player script
  89. status = script.status(Player_Script_Name)
  90. if status == false then
  91. -- Enable player script
  92. script.enable(Player_Script_Name)
  93. end
  94. -- Exit script
  95. return
  96. end
  97.  
  98. -- Delete all recorded data
  99. if modus == 16 then
  100. -- Delete all recorded data from storage
  101. storage.set(Storage_Name, nil)
  102. alert('All recorded simulation data is deleted')
  103. -- Turn off feedback LED
  104. -- Get start / stop feedback value
  105. A_P_S = grp.getvalue(Address_Play_Stop_Feedback)
  106. if A_P_S == true then
  107. -- Disable start / stop feedback LED
  108. grp.update(Address_Play_Stop_Feedback, false)
  109. end
  110. -- Exit script
  111. return
  112. end
  113.  
  114. -- Make sure events from Address_Modus are not recorded
  115. if event.dst == Address_Modus then
  116. -- Turn off feedback LED
  117. -- Get start / stop feedback value
  118. A_P_S = grp.getvalue(Address_Play_Stop_Feedback)
  119. if A_P_S == true then
  120. -- Disable start / stop feedback LED
  121. grp.update(Address_Play_Stop_Feedback, false)
  122. end
  123. -- Exit script
  124. return
  125. end
  126.  
  127. -- Make sure events are not recorded while recorder is inactive, player is active or deletion mode is selected
  128. if modus == 0 or modus == 15 or modus == 16 then
  129. -- Exit script
  130. return
  131. end
  132.  
  133. -- When modus is 1 to 14 then this will be executed:
  134.  
  135. -- Get object last change timestamp to determine recording day
  136. objecttimestamp = objectinfo.updatetime
  137. objecttimetable = os.date("*t", objecttimestamp)
  138.  
  139. -- Get Modus last change timestamp to determine recording day
  140. modusinfo = grp.find(Address_Modus)
  141. modustimestamp = modusinfo.updatetime
  142. modustimetable = os.date("*t", modustimestamp)
  143.  
  144. if modus == 1 then -- Record Today
  145. if objecttimetable.yday == modustimetable.yday and objecttimetable.year == modustimetable.year then
  146. -- Continue Script
  147. else
  148. -- Exit Script
  149. return
  150. end
  151. elseif modus == 2 then -- Record Sonday
  152. if objecttimetable.wday == 1 and (objecttimestamp - modustimestamp) < 604800 then
  153. -- Continue Script
  154. else
  155. -- Exit Script
  156. return
  157. end
  158. elseif modus == 3 then -- Record Monday
  159. if objecttimetable.wday == 2 and (objecttimestamp - modustimestamp) < 604800 then
  160. -- Continue Script
  161. else
  162. -- Exit Script
  163. return
  164. end
  165. elseif modus == 4 then -- Record Tuesday
  166. if objecttimetable.wday == 3 and (objecttimestamp - modustimestamp) < 604800 then
  167. -- Continue Script
  168. else
  169. -- Exit Script
  170. return
  171. end
  172. elseif modus == 5 then -- Record Wednesday
  173. if objecttimetable.wday == 4 and (objecttimestamp - modustimestamp) < 604800 then
  174. -- Continue Script
  175. else
  176. -- Exit Script
  177. return
  178. end
  179. elseif modus == 6 then -- Record Thursday
  180. if objecttimetable.wday == 5 and (objecttimestamp - modustimestamp) < 604800 then
  181. -- Continue Script
  182. else
  183. -- Exit Script
  184. return
  185. end
  186. elseif modus == 7 then -- Record Friday
  187. if objecttimetable.wday == 6 and (objecttimestamp - modustimestamp) < 604800 then
  188. -- Continue Script
  189. else
  190. -- Exit Script
  191. return
  192. end
  193. elseif modus == 8 then -- Record Saturday
  194. if objecttimetable.wday == 7 and (objecttimestamp - modustimestamp) < 604800 then
  195. -- Continue Script
  196. else
  197. -- Exit Script
  198. return
  199. end
  200. elseif modus == 9 then -- Record Midweek
  201. 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
  202. -- Continue Script
  203. else
  204. -- Exit Script
  205. return
  206. end
  207. elseif modus == 10 then -- Record Weekend
  208. if objecttimetable.wday == 1 or objecttimetable.wday == 7 and (objecttimestamp - modustimestamp) < 604800 then
  209. -- Continue Script
  210. else
  211. -- Exit Script
  212. return
  213. end
  214. elseif modus == 11 then -- Record Full Week from Activation
  215. if (objecttimestamp - modustimestamp) < 604800 then
  216. -- Continue Script
  217. else
  218. -- Exit Script
  219. return
  220. end
  221. elseif modus == 12 then -- Record Full Week from Day 1
  222. if ((modustimetable.yday - modustimetable.wday) + 7) == (updatetimetable.yday - updatetimetable.wday) and (objecttimestamp - modustimestamp) < 604800 then
  223. -- Continue Script
  224. else
  225. -- Exit Script
  226. return
  227. end
  228. elseif modus == 13 or modus == 14 then -- Record Alway
  229. -- Continue Script
  230. end
  231.  
  232. -- Get previous data from storage if available
  233. Storage_Data = storage.get(Storage_Name)
  234. -- Check if storagedata is available
  235. if Storage_Data == nil then
  236. -- Create new data table
  237. Storage_Data = {}
  238. table.insert(Storage_Data, {updatetime = objectinfo.updatetime, address = objectinfo.address, value = objectinfo.value, recordmodus = modus, lastplayed = os.time(), timelap = (objectinfo.updatetime - modusinfo.updatetime)})
  239. else
  240. -- Loop through array and clear to all found items older then selected scope or from other modus in the recorded values
  241. for index, StorageData in pairs(Storage_Data) do
  242. -- Check value before write (if enabled)
  243. if StorageData.recordtime == nil then
  244. -- Do nothing
  245. else
  246. if (os.time() - StorageData.recordtime) > 604800 or StorageData.recordmodus ~= modus then
  247. -- Delete item
  248. Storage_Data[index]=nil
  249. end
  250. end
  251. end
  252. -- Add data to excisting data table
  253. 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)})
  254. end
  255.  
  256. -- Write new data back to storage
  257. storage.set(Storage_Name, Storage_Data)
  258. log (Storage_Data)
  259. else
  260.  
  261. -- Get current script status
  262. Script_Status = script.status(Player_Script_Name)
  263. if Script_Status == true then
  264. -- Disable player script
  265. script.disable(Player_Script_Name)
  266. end
  267.  
  268. -- Turn off feedback LED
  269. -- Get start / stop feedback value
  270. A_P_S = grp.getvalue(Address_Play_Stop_Feedback)
  271. if A_P_S == true then
  272. -- Disable start / stop feedback LED
  273. grp.update(Address_Play_Stop_Feedback, false)
  274. end
  275.  
  276. 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.

Source code    
  1. -- Simulation Module Version 2.0 - Player Part
  2. -- Created by Erwin van der Zwart
  3.  
  4. -- ********** SET PARAMETERS ***********
  5.  
  6. -- Set storage name for recorded objects
  7. Storage_Name = 'STSIMULATION'
  8.  
  9. -- Check object if object value is changed before write (same value = no write)
  10. Check_Object_Changed = false
  11.  
  12. -- 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.
  13. 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.
  14.  
  15. -- ********** END PARAMETERS ***********
  16.  
  17. -- Get data from storage if available
  18. Storage_Data = storage.get(Storage_Name)
  19.  
  20. -- Set Data table changed to false (save data only when changes are detected)
  21. Data_Table_Changed = false
  22.  
  23. -- Check if storagedata is available
  24. if Storage_Data == nil then
  25. -- Exit Script
  26. return
  27. else
  28. -- Sort table by updatetime
  29. table.sort(Storage_Data, function(first,last) return first.updatetime < last.updatetime end)
  30.  
  31. -- Set current time as timestamp for calculation
  32. timestamp = os.time()
  33.  
  34. -- Set counter to 0
  35. counter = 0
  36.  
  37. -- Loop through array and write to all found addresses in this recording the values
  38. for index, StorageData in ipairs(Storage_Data) do
  39. ::ObjectStart::
  40.  
  41. -- Create random value to play object not exactly on same time but in a scope of 30 minutes from that time.
  42. random = math.random(0, Sec_Random)
  43.  
  44. -- Create table from current time to get wday and yday
  45. currenttimetable = os.date("*t", os.time())
  46.  
  47. -- Create table from lastplayed to get wday and yday
  48. lastplayedtable = os.date("*t", StorageData.lastplayed)
  49.  
  50. -- Check if recordmodus is available
  51. if StorageData.recordmodus == nil then
  52. -- Do nothing, move to next object because this object has no modus info.
  53. counter = counter + StorageData.timelap
  54. goto NextObject
  55. else
  56. modus = StorageData.recordmodus
  57. end
  58.  
  59. -- Check if weekday matches with recording (not for modus 1 and 14)
  60. if modus ~= 1 or modus ~= 14 then
  61. if currenttimetable.wday == lastplayedtable.wday then
  62. -- Day matches, move on with script
  63. else
  64. -- Do nothing, move to next object because this object doesn't mach day of playing.
  65. counter = counter + StorageData.timelap
  66. goto NextObject
  67. end
  68. end
  69.  
  70. -- Check value before write (if enabled)
  71. if Check_Object_Changed == true then
  72. -- Get current object value
  73. value = grp.getvalue(StorageData.address)
  74. -- Check if value is the same
  75. if value == StorageData.value then
  76. -- Do nothing, move to next object because this object aready is set to wanted value.
  77. counter = counter + StorageData.timelap
  78. goto NextObject
  79. end
  80. end
  81.  
  82. -- Selected actions by modus
  83. if modus == 14 then -- Play Direct (for testing your recordings)
  84. -- Check if this is first object from table
  85. if index == 1 then
  86. if (timestamp + StorageData.timelap) <= os.time() then
  87. -- Move to write to object part
  88. counter = counter + StorageData.timelap
  89. else
  90. -- Slow looping down when object does not meet requirements (only for modus 14)
  91. os.sleep (1)
  92. -- Try again
  93. goto ObjectStart
  94. end
  95. else
  96. if ((timestamp + StorageData.timelap) + counter) <= os.time() then
  97. -- Move to write to object part
  98. counter = counter + StorageData.timelap
  99. else
  100. -- Slow looping down when object does not meet requirements (only for modus 14)
  101. os.sleep (1)
  102. -- Try again
  103. goto ObjectStart
  104. end
  105. end
  106. end
  107.  
  108. -- ** Write to object part ** (all previous conditions are true)
  109.  
  110. -- Write function for modus 1 to 13 to use when conditions are true
  111. function WritetoObject_Modus_1_to_13()
  112. -- Calculate if object is at least 12 hrs old (to make sure it won't be played again the same day)
  113. if os.time() - StorageData.lastplayed >= 43200 then
  114. timedifference = os.time() - StorageData.updatetime
  115. -- Perform modulo 86400 to clear all passed days from time difference and keep time difference < 24hrs
  116. result = timedifference % 86400
  117. -- Calculate seconds to complete minutes past
  118. result = math.floor(result / 60)
  119. -- Calculate random to complete minutes to calculate
  120. random = math.floor(random / 60)
  121. -- Create a random time between 30 minutes to meet condition to play
  122. if result >= (1440 - random) then -- 1440 = 60 min * 24 hrs
  123. -- Write address to wanted value
  124. grp.write(StorageData.address, StorageData.value)
  125. -- Update timestamp last played
  126. StorageData.lastplayed = os.time()
  127. Data_Table_Changed = true
  128. end
  129. end
  130. end
  131.  
  132. -- Write function for modus 14 to use when conditions are true
  133. function WritetoObject_Modus_14()
  134. grp.write(StorageData.address, StorageData.value)
  135. end
  136.  
  137. -- Determine what objects to play on recording conditions
  138. if modus == 1 then -- Today
  139. WritetoObject_Modus_1_to_13()
  140. elseif modus == 2 and currenttimetable.wday == 1 then -- Sunday
  141. WritetoObject_Modus_1_to_13()
  142. elseif modus == 3 and currenttimetable.wday == 2 then -- Monday
  143. WritetoObject_Modus_1_to_13()
  144. elseif modus == 4 and currenttimetable.wday == 3 then -- Tuesday
  145. WritetoObject_Modus_1_to_13()
  146. elseif modus == 5 and currenttimetable.wday == 4 then -- Wednesday
  147. WritetoObject_Modus_1_to_13()
  148. elseif modus == 6 and currenttimetable.wday == 5 then -- Thursday
  149. WritetoObject_Modus_1_to_13()
  150. elseif modus == 7 and currenttimetable.wday == 6 then -- Vriday
  151. WritetoObject_Modus_1_to_13()
  152. elseif modus == 8 and currenttimetable.wday == 7 then -- Saterday
  153. WritetoObject_Modus_1_to_13()
  154. 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
  155. WritetoObject_Modus_1_to_13()
  156. elseif modus == 10 and (currenttimetable.wday == 1 or currenttimetable.wday == 7) then -- Weekend
  157. WritetoObject_Modus_1_to_13()
  158. 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
  159. WritetoObject_Modus_1_to_13()
  160. elseif modus == 14 then -- Play direct
  161. WritetoObject_Modus_14()
  162. end
  163. ::NextObject::
  164. end
  165. -- Write changed table back to storage when timestamp lastplayed is changed during playing
  166. if Data_Table_Changed == true then
  167. -- Write new data back to storage
  168. storage.set(Storage_Name, Storage_Data)
  169. end
  170. 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