· 4 years ago · Dec 15, 2020, 06:18 PM
1package.preload['common.c4_common'] = (function (...)
2--[[=============================================================================
3 ON_INIT, Timer,s and Property management functions
4
5 Copyright 2016 Control4 Corporation. All Rights Reserved.
6===============================================================================]]
7require "common.c4_driver_declarations"
8require "lib.c4_log"
9require "lib.c4_timer"
10
11-- Set template version for this file
12if (TEMPLATE_VERSION ~= nil) then
13 TEMPLATE_VERSION.c4_common = "2016.01.08"
14end
15
16--[[=============================================================================
17 Create and Initialize Logging
18===============================================================================]]
19function ON_DRIVER_EARLY_INIT.c4_common()
20 -- Create a logger
21 LOG = c4_log:new("Template_c4z Change Name")
22end
23
24function ON_DRIVER_INIT.c4_common()
25 -- Create Log Timer
26 gC4LogTimer = c4_timer:new("Log Timer", 45, "MINUTES", OnLogTimerExpired)
27end
28
29--[[=============================================================================
30 Log timer callback function
31===============================================================================]]
32function OnLogTimerExpired()
33 LogWarn("Turning Log Mode Off (timer expired)")
34 gC4LogTimer:KillTimer()
35
36 C4:UpdateProperty("Log Mode", "Off")
37 OnPropertyChanged("Log Mode")
38end
39
40gForceLogging = false
41function ON_PROPERTY_CHANGED.LogMode(propertyValue)
42 gC4LogTimer:KillTimer()
43
44 if (gForceLogging) then
45 LOG:OutputPrint(true)
46 LOG:OutputC4Log(true)
47 else
48 LOG:OutputPrint(propertyValue:find("Print") ~= nil)
49 LOG:OutputC4Log(propertyValue:find("Log") ~= nil)
50 if (propertyValue == "Off") then
51 return
52 end
53
54 gC4LogTimer:StartTimer()
55 end
56end
57
58function ON_PROPERTY_CHANGED.LogLevel(propertyValue)
59 if (gForceLogging) then
60 LOG:SetLogLevel("5 - Debug")
61 else
62 LOG:SetLogLevel(propertyValue)
63 end
64end
65
66--[[=============================================================================
67 Print Template Versions
68===============================================================================]]
69function TemplateVersion()
70 print ("\nTemplate Versions")
71 print ("-----------------------")
72 for k, v in pairs(TEMPLATE_VERSION) do
73 print (k .. " = " .. v)
74 end
75
76 print ("")
77end end)
78package.preload['common.c4_conditional'] = (function (...)
79--[[=============================================================================
80 Functions for handling conditionals in control4 project programming
81
82 Copyright 2017 Control4 Corporation. All Rights Reserved.
83===============================================================================]]
84
85require "common.c4_driver_declarations"
86
87-- Set template version for this file
88if (TEMPLATE_VERSION ~= nil) then
89 TEMPLATE_VERSION.c4_conditional = "2017.04.25"
90end
91
92function TestCondition(ConditionalName, tParams)
93 LogTrace("TestCondition() : %s", tostring(ConditionalName))
94-- LOG:Trace(tParams)
95
96 local retVal = false
97 local callSuccess = false
98 local trimmedConditionalName = string.gsub(ConditionalName, " ", "")
99
100 if (PROG_CONDITIONAL[ConditionalName] ~= nil and type(PROG_CONDITIONAL[ConditionalName]) == "function") then
101 callSuccess, retVal = pcall(PROG_CONDITIONAL[ConditionalName], tParams)
102
103 -- elseif trimmed function exists then execute
104 elseif (PROG_CONDITIONAL[trimmedConditionalName] ~= nil and type(PROG_CONDITIONAL[trimmedConditionalName]) == "function") then
105 callSuccess, retVal = pcall(PROG_CONDITIONAL[trimmedConditionalName], tParams)
106
107 else
108 LogInfo("TestCondition: Unhandled condition = %s", tostring(ConditionalName))
109 end
110
111 if (not callSuccess) then
112 LogError("LUA_ERROR: %s", tostring(retVal))
113 end
114
115 LogTrace("Result = " .. tostring(retVal))
116 return retVal
117end
118
119 end)
120package.preload['common.c4_device_connection_base'] = (function (...)
121--[[=============================================================================
122 DeviceConnectionBase Class
123
124 Copyright 2016 Control4 Corporation. All Rights Reserved.
125===============================================================================]]
126require "common.c4_common"
127require "lib.c4_object"
128require "lib.c4_log"
129require "lib.c4_timer"
130require "lib.c4_queue"
131
132-- Set template version for this file
133if (TEMPLATE_VERSION ~= nil) then
134 TEMPLATE_VERSION.c4_device_connection_base = "2016.01.08"
135end
136
137COMMAND_QUEUE_SIZE = 100
138DEFAULT_COMMAND_DELAY_INTERVAL = 100 -- Don't send consecutive commands faster than this many milliseconds
139DEFAULT_COMMAND_RESPONSE_INTERVAL = 3 -- If we haven't received and ACK after this many seconds, try again
140DEFAULT_RETRY_COUNT_MAX = 3
141
142function ON_DRIVER_EARLY_INIT.c4_device_connection_base()
143 gReceiveBuffer = ""
144 gIsUrlConnected = false
145 gIsNetworkServerConnected = false
146 gIsNetworkConnected = false
147 gIsSerialConnected = false
148 gIsIRConnected = false
149end
150
151DeviceConnectionBase = inheritsFrom(nil)
152
153function DeviceConnectionBase:construct()
154
155 self._IsConnected = false
156 self._SendTimer = nil
157 self._WaitResponseTimer = nil
158 self._CommandQueue = nil
159 self._Priority1CommandQueue = nil
160 self._Priority2CommandQueue = nil
161 self._LastCommand = nil
162 self._ExpectAck = false
163 self._CommandRetryCount = 0
164 self._RetryCountMax = DEFAULT_RETRY_COUNT_MAX
165
166 self._SendCommandDelayMS = DEFAULT_COMMAND_DELAY_INTERVAL
167 self._CommandResponseWaitS = DEFAULT_COMMAND_RESPONSE_INTERVAL
168
169 -- Polling
170 self._PollingInterval = 0
171 self._PollingUnits = "SECONDS"
172 self._PollingTimer = nil
173end
174
175function DeviceConnectionBase:Initialize(ExpectAck, CommandDelayInterval, CommandResponseInterval, CallbackParam)
176
177 if (ExpectAck ~= nil) then
178 self._ExpectAck = ExpectAck
179 end
180
181 if (CommandDelayInterval ~= nil) then
182 self._SendCommandDelayMS = CommandDelayInterval
183 end
184
185 if (CommandResponseInterval ~= nil) then
186 self._CommandResponseWaitS = CommandResponseInterval
187 end
188
189 self._CommandQueue = c4_queue:new()
190 self._CommandQueue:SetMaxSize(COMMAND_QUEUE_SIZE)
191 self._CommandQueue:SetName("Command Queue")
192
193 self._Priority1CommandQueue = c4_queue:new()
194 self._Priority1CommandQueue:SetMaxSize(COMMAND_QUEUE_SIZE)
195 self._Priority1CommandQueue:SetName("P1 Queue")
196
197 self._Priority2CommandQueue = c4_queue:new()
198 self._Priority2CommandQueue:SetMaxSize(COMMAND_QUEUE_SIZE)
199 self._Priority2CommandQueue:SetName("P2 Queue")
200
201 -- usually only one of these timers will be used, but it's pretty low overhead to instantiate both of them
202 self._SendTimer = c4_timer:new("SendCommand", self._SendCommandDelayMS, "MILLISECONDS", DeviceConnectionBase.OnSendTimeExpired, false, CallbackParam)
203 self._WaitResponseTimer = c4_timer:new("WaitResponse", self._CommandResponseWaitS, "SECONDS", DeviceConnectionBase.OnWaitTimeExpired, false, CallbackParam)
204end
205
206function DeviceConnectionBase:InitPolling(PollingInterval, PollingUnits, CallbackParam)
207 LogFatal("DeviceConnectionBase:InitPolling()")
208 if (PollingInterval ~= nil) then
209 self._PollingInterval = PollingInterval
210 end
211
212 self._PollingUnits = PollingUnits or self._PollingUnits
213
214 LogFatal("self._PollingInterval: %s, self._PollingUnits: %s", tostring(self._PollingInterval), tostring(self._PollingUnits))
215
216 -- create polling timer
217 self._PollingTimer = c4_timer:new("Polling", self._PollingInterval, self._PollingUnits, DeviceConnectionBase.OnPollingTimerExpired, false, CallbackParam)
218end
219
220function DeviceConnectionBase:StartPolling(interval, units)
221 LogFatal("DeviceConnectionBase:StartPolling()")
222 LogFatal("self._PollingTimer: %s", tostring(self._PollingTimer))
223
224 if (self._PollingTimer ~= nil) then
225 self._PollingTimer:KillTimer()
226
227 local timer_units = units or self._PollingTimer._units
228 local timer_interval = interval or self._PollingInterval
229
230 self._PollingTimer:StartTimer(timer_interval, timer_units)
231 end
232end
233
234function DeviceConnectionBase:StopPolling()
235 LogFatal("DeviceConnectionBase:StopPolling()")
236 self._PollingTimer:KillTimer()
237end
238
239function DeviceConnectionBase:SetExpectACK(ExpectACK)
240 self._ExpectAck = ExpectACK
241end
242
243function DeviceConnectionBase:SetCommandDelayInterval(DelayInterval)
244 self._SendCommandDelayMS = DelayInterval
245end
246
247function DeviceConnectionBase:SetResponseWaitInterval(WaitInterval)
248 self._CommandResponseWaitS = WaitInterval
249end
250
251function DeviceConnectionBase:ReceivedFromCom(sData)
252
253 gReceiveBuffer = gReceiveBuffer .. sData
254 LogTrace("ReceivedFromCom ReceiveBuffer is now {{{%s}}}", gReceiveBuffer)
255
256 message = self:GetMessage()
257 while (message ~= nil and message ~= "") do
258 status, err = pcall(HandleMessage, message)
259 if (status) then
260 message = self:GetMessage()
261 else
262 LogError("LUA_ERROR: " .. err)
263 message = ""
264 gReceiveBuffer = ""
265 end
266 end
267end
268
269function DeviceConnectionBase:SetConnection(IsConnected, method)
270 self._IsConnected = IsConnected
271 gControlMethod = method
272end
273
274function DeviceConnectionBase:ControlMethod()
275 -- Override in derived class
276 print("WARNING: Need to override ControlMethod - should never be called")
277
278 return ""
279end
280
281function DeviceConnectionBase:StartCommandTimer(...)
282 local value = select(1, ...)
283 local units = select(2, ...)
284 local command_name = select(3, ...) or ""
285
286 self._WaitResponseTimer:KillTimer()
287 self._SendTimer:KillTimer()
288
289 if (self._ExpectAck) then
290 -- expecting an ACK set the Response Wait timer
291 local timer_units = units or self._WaitResponseTimer._units
292 local timer_interval = value or self._CommandResponseWaitS
293
294 self._WaitResponseTimer:StartTimer(timer_interval, timer_units)
295 LogTrace(string.format("Starting wait Timer: %d", self._WaitResponseTimer._timerID) .. " for " .. command_name)
296 else
297 -- no ACK expected, just wait the designated amount of time and send another command
298 local timer_units = units or self._SendTimer._units
299 local timer_interval = value or self._SendCommandDelayMS
300
301 self._SendTimer:StartTimer(timer_interval, timer_units)
302 LogTrace(string.format("Starting Send Timer: %d for %s (timer_interval = %d, timer_units = %s)", self._SendTimer._timerID, command_name, timer_interval, timer_units))
303 end
304end
305
306-- Note the '.' instead of the ':'
307function DeviceConnectionBase.OnSendTimeExpired(Instance)
308 LogTrace("Send Timer expired")
309 Instance._SendTimer:KillTimer()
310
311 local tCommand = Instance._LastCommand
312 if (tCommand ~= nil) then
313 if (tCommand.command_name ~= nil) then
314 LogTrace("Send Timer expired - Last Command: %s, Send Next Command", tostring(tCommand.command_name))
315 elseif (type(tCommand) == "string") then
316 LogTrace("Send Timer expired - Last Command: %s, Send Next Command", tostring(tCommand))
317 end
318 else
319 LogTrace("Send Timer expired - Last Command: UNKNOWN, Send Next Command")
320 end
321
322 Instance._LastCommand = nil
323 Instance:SendNextCommand()
324
325 if (DoEvents ~= nil and type(DoEvents) == "function") then
326 DoEvents()
327 end
328end
329
330function DeviceConnectionBase.OnWaitTimeExpired(Instance)
331 LogTrace("Wait Timer expired")
332 Instance._WaitResponseTimer:KillTimer()
333 Instance._CommandRetryCount = Instance._CommandRetryCount + 1
334
335 if (Instance._CommandRetryCount >= Instance._RetryCountMax) then
336 -- To many retries, pop the current command and try the next one
337 Instance._CommandRetryCount = 0
338 Instance:SendNextCommand()
339 else
340 Instance:SendLastCommand()
341 end
342end
343
344function DeviceConnectionBase.OnPollingTimerExpired(Instance)
345 LogTrace("Polling Timer expired")
346 Instance._PollingTimer:KillTimer()
347
348 OnPollingTimerExpired()
349
350 Instance._PollingTimer:StartTimer(Instance._PollingInterval)
351end
352
353function DeviceConnectionBase:HandleACK()
354 self._LastCommand = nil
355
356 self._WaitResponseTimer:KillTimer()
357 self._CommandRetryCount = 0
358 self:SendNextCommand()
359end
360
361function DeviceConnectionBase:QueueEmpty()
362 return (self._CommandQueue:empty() and self._Priority1CommandQueue:empty() and self._Priority2CommandQueue:empty())
363end
364
365function DeviceConnectionBase:QueueCommand(sCommand, ...)
366-- LogTrace("QueueCommand(%s)", sCommand)
367 local command_delay = select(1, ...)
368 local delay_units = select(2, ...)
369 local command_name = select(3, ...)
370
371 if (sCommand == nil) or (sCommand == "") then
372 return
373 end
374
375 if (self._LastCommand == nil) then
376 self._CommandQueue:push(sCommand, command_delay, delay_units, command_name)
377 self._LastCommand = self._CommandQueue:pop()
378 self:SendCommand(sCommand, command_delay, delay_units, command_name)
379 else
380 self._CommandQueue:push(sCommand, command_delay, delay_units, command_name)
381 end
382end
383
384function DeviceConnectionBase:QueuePriority1Command(sCommand, ...)
385 LogTrace("QueuePriority1Command(%s)", sCommand)
386 local command_delay = select(1, ...)
387 local delay_units = select(2, ...)
388 local command_name = select(3, ...)
389
390 if (sCommand == nil) or (sCommand == "") then
391 return
392 end
393
394 if (self._LastCommand == nil) then
395 self._Priority1CommandQueue:push(sCommand, command_delay, delay_units, command_name)
396 self._LastCommand = self._Priority1CommandQueue:pop()
397 self:SendCommand(sCommand, command_delay, delay_units, command_name)
398 else
399 self._Priority1CommandQueue:push(sCommand, command_delay, delay_units, command_name)
400 end
401end
402
403function DeviceConnectionBase:QueuePriority2Command(sCommand, ...)
404 LogTrace("QueuePriority2Command(%s)", sCommand)
405 local command_delay = select(1, ...)
406 local delay_units = select(2, ...)
407 local command_name = select(3, ...)
408
409 if (sCommand == nil) or (sCommand == "") then
410 return
411 end
412
413 if (self._LastCommand == nil) then
414 self._Priority2CommandQueue:push(sCommand, command_delay, delay_units, command_name)
415 self._LastCommand = self._Priority2CommandQueue:pop()
416 self:SendCommand(sCommand, command_delay, delay_units, command_name)
417 else
418 self._Priority2CommandQueue:push(sCommand, command_delay, delay_units, command_name)
419 end
420end
421
422function DeviceConnectionBase:SendNextCommand()
423 LogTrace("DeviceConnectionBase:SendNextCommand")
424
425 local tCommand = nil
426 if (not self._Priority1CommandQueue:empty()) then
427 tCommand = self._Priority1CommandQueue:pop()
428 LogTrace(tostring(gCon._Priority1CommandQueue))
429 elseif (not self._Priority2CommandQueue:empty()) then
430 tCommand = self._Priority2CommandQueue:pop()
431 LogTrace(tostring(gCon._Priority2CommandQueue))
432 elseif (not self._CommandQueue:empty()) then
433 tCommand = self._CommandQueue:pop()
434 LogTrace(tostring(gCon._CommandQueue))
435 end
436
437 if (tCommand ~= nil) then
438 self._LastCommand = tCommand
439 local sCommand = tCommand.command
440 local command_delay = tCommand.command_delay
441 local delay_units = tCommand.delay_units
442 local command_name = tCommand.command_name
443
444 if (sCommand == nil or sCommand == "") then
445 self._SendTimer:KillTimer()
446 self._WaitResponseTimer:KillTimer()
447 else
448 LogTrace("SendCommand: %s", sCommand)
449 self:SendCommand(sCommand, command_delay, delay_units, command_name)
450 end
451 end
452end
453
454function DeviceConnectionBase:SendLastCommand()
455-- LogTrace("DeviceConnectionBase:SendLastCommand")
456
457 local tCommand = self._LastCommand
458 if (tCommand ~= nil) then
459 local sCommand = tCommand.command
460 local command_delay = tCommand.command_delay
461 local delay_units = tCommand.delay_units
462 local command_name = tCommand.command_name
463
464 if (sCommand == nil or sCommand == "") then
465 self._SendTimer:KillTimer()
466 self._WaitResponseTimer:KillTimer()
467 else
468 LogTrace("SendCommand: %s", sCommand)
469 self:SendCommand(sCommand, command_delay, delay_units, command_name)
470 end
471 end
472end
473
474function DeviceConnectionBase:SendCommand()
475 -- Dummy routine. Override in derived class
476 print("Need to override SendCommand - should never be called")
477end
478
479
480function DeviceConnectionBase:GetMessage()
481
482 -- Brain dead version of this routine. Just return the current receive buffer.
483 -- It's very likely that a GetMessage() function will need to be created
484 if (GetMessage ~= nil and type(GetMessage) == "function") then
485 return GetMessage()
486 else
487 local ComMessage = gReceiveBuffer
488 gReceiveBuffer = ""
489
490 return ComMessage
491 end
492end
493
494--[[=============================================================================
495 Other Connection Functions
496===============================================================================]]
497
498function ReceivedFromSerial(idBinding, sData)
499 if (gCon.ReceivedFromSerial == nil) then return end --serial is bound but not the current control method
500 gCon:ReceivedFromSerial(idBinding, sData)
501end
502
503function ReceivedFromNetwork(idBinding, nPort, sData)
504 gCon:ReceivedFromNetwork(idBinding, nPort, sData)
505end
506
507function OnServerDataIn(nHandle, strData)
508-- LogTrace("Received Data on Handle: " .. nHandle .. ": " .. strData)
509-- LogTrace("Data Is: %s", HexToString(strData))
510 gCon:ReceivedFromNetworkServer(nHandle, strData)
511end
512
513
514--[[=============================================================================
515 The ReceivedAsync function is called in response to 'url_get_request'.
516 The ticketId is the number returned from the request.
517===============================================================================]]
518function ReceivedAsync(ticketId, strData, responseCode, tHeaders)
519 strData = strData or ""
520 responseCode = responseCode or 0
521 tHeaders = tHeaders or {}
522
523-- LogTrace("ReceivedAsync[" .. ticketId .. "]: Response Code: " .. responseCode .. " Length: " .. string.len(strData))
524-- LogTrace(tHeaders)
525
526 gCon:ReceivedAsync(ticketId, strData, responseCode, tHeaders)
527end
528
529--[[=============================================================================
530 OnBindingChanged(idBinding, class, bIsBound)
531
532 Description:
533 Function called by Director when a binding changes state(bound or unbound).
534
535 Parameters:
536 idBinding(int) - ID of the binding whose state has changed.
537 class(string) - Class of binding that has changed.
538 A single binding can have multiple classes(i.e. COMPONENT,
539 STEREO, RS_232, etc).
540 This indicates which has been bound or unbound.
541 bIsBound(bool) - Whether the binding has been bound or unbound.
542
543 Returns:
544 None
545===============================================================================]]
546function OnBindingChanged(idBinding, class, bIsBound)
547
548 LogTrace("OnBindingChanged(): idBinding = " .. tostring(idBinding) .. ", class = " .. class .. ", bIsBound = " .. tostring(bIsBound))
549 if (idBinding == SERIAL_BINDING_ID) then
550 gIsSerialConnected = bIsBound
551 SetControlMethod()
552 OnSerialConnectionChanged(idBinding, class, bIsBound)
553 elseif (idBinding == IR_BINDING_ID) then
554 gIsIRConnected = bIsBound
555 SetControlMethod()
556 OnIRConnectionChanged(idBinding, class, bIsBound)
557 elseif(OnConnectionChanged ~= nil and type(OnConnectionChanged) == "function") then
558 OnConnectionChanged(idBinding, class, bIsBound)
559 end
560end
561
562--[[=============================================================================
563 OnNetworkBindingChanged(idBinding, bIsBound)
564
565 Description:
566 Function called by Director when a network binding changes state(bound or unbound).
567
568 Parameters:
569 idBinding(int) - ID of the binding whose state has changed.
570 bIsBound(bool) - Whether the binding has been bound or unbound.
571
572 Returns:
573 None
574===============================================================================]]
575function OnNetworkBindingChanged(idBinding, bIsBound)
576 LogTrace('OnNetworkBindingChanged(): idBinding = ' .. tostring(idBinding) .. ' bIsBound = ' .. tostring(bIsBound))
577
578 gIsNetworkConnected = bIsBound
579 SetControlMethod()
580 OnNetworkConnectionChanged(idBinding, bIsBound)
581 if (bIsBound) then
582 -- Start a special instance of reconnect timer to eventually do NetConnect if not done automatically
583 gCon._NetworkReconnectTimer:StartTimer(gNetworkReconnectInterval)
584 end
585end
586
587--[[=============================================================================
588 OnConnectionStatusChanged(idBinding, nPort, sStatus)
589
590 Description:
591 Sets the updated status of the specified binding
592
593 Parameters:
594 idBinding(int) - ID of the binding whose status has changed
595 nPort(int) - The communication port of the specified bindings connection
596 sStatus(string) - "ONLINE" if the connection status is to be set to Online,
597 any other value will set the status to Offline
598
599 Returns:
600 None
601===============================================================================]]
602function OnConnectionStatusChanged(idBinding, nPort, sStatus)
603 LogTrace("OnConnectionStatusChanged[" .. idBinding .. " (" .. tostring(nPort) .. ")]: " .. sStatus)
604
605 local isOnline = false
606
607 gNetworkStatus = sStatus
608 if (sStatus == "ONLINE") then
609 isOnline = true
610 end
611
612 gCon:SetOnlineStatus(isOnline)
613 OnNetworkStatusChanged(idBinding, nPort, sStatus)
614end
615
616--[[=============================================================================
617 SetControlMethod()
618
619 Description:
620 Sets the control method type for the drivers internal infrastructure
621
622 Parameters:
623 None
624
625 Returns:
626 The type of control method for the drivers connection(i.e. Network, Serial,
627 IR, or (none))
628===============================================================================]]
629function SetControlMethod()
630 if (gCon ~= nil) then
631 if (gIsNetworkConnected == false) and (gCon._NetworkReconnectTimer ~= nil) then
632 --housekeeping when changing from network control to serial or IR control
633 gCon._NetworkReconnectTimer:KillTimer()
634 end
635 end
636
637
638 if( gIsNetworkServerConnected) then
639 -- connect to NetworkServer communicator if not already connected
640 if (gCon == nil or gCon.ControlMethod() ~= "NetworkServer") then
641 gCon = NetworkServerConnectionBase:new()
642 gCon:Initialize()
643 end
644 gCon:SetConnection(true, "NetworkServer")
645 elseif (gIsNetworkConnected) then
646 -- connect to Network communicator if not already connected
647 if (gCon == nil or gCon.ControlMethod() ~= "Network") then
648 gCon = NetworkConnectionBase:new(NETWORK_BINDING_ID, NETWORK_PORT)
649 gCon:Initialize(COM_USE_ACK, COM_COMMAND_DELAY_MILLISECONDS, COM_COMMAND_RESPONSE_TIMEOUT_SECONDS)
650 end
651 gCon:SetConnection(true, "Network")
652 elseif (gIsUrlConnected) then
653 -- connect to URL communicator if not already connected
654 if (gCon == nil or gCon.ControlMethod() ~= "URL") then
655 gCon = UrlConnectionBase:new()
656 gCon:Initialize(COM_USE_ACK, COM_COMMAND_DELAY_MILLISECONDS, COM_COMMAND_RESPONSE_TIMEOUT_SECONDS)
657 end
658 gCon:SetConnection(true, "URL")
659 elseif (gIsSerialConnected) then
660 -- connect to Serial communicator if not already connected
661 if (gCon == nil or gCon.ControlMethod() ~= "Serial") then
662 gCon = SerialConnectionBase:new(SERIAL_BINDING_ID)
663 gCon:Initialize(COM_USE_ACK, COM_COMMAND_DELAY_MILLISECONDS, COM_COMMAND_RESPONSE_TIMEOUT_SECONDS)
664 gCon:InitPolling(tonumber(gPollingTimerInterval), "MINUTES", gCon)
665 end
666 gCon:SetConnection(true, "Serial")
667 elseif (gIsIRConnected) then
668 -- connect to IR communicator if not already connected
669 if (gCon == nil or gCon.ControlMethod() ~= "IR") then
670 gCon = IRConnectionBase:new(IR_BINDING_ID)
671 gCon:Initialize(COM_USE_ACK, COM_COMMAND_DELAY_MILLISECONDS, COM_COMMAND_RESPONSE_TIMEOUT_SECONDS)
672 end
673 gCon:SetConnection(true, "IR")
674 else
675 if (gCon ~= nil) then
676 gCon:SetConnection(false, "(none)")
677 end
678 -- gCon = nil
679 end
680
681 gCon._CommandQueue:clear()
682 gCon._Priority1CommandQueue:clear()
683 gCon._Priority2CommandQueue:clear()
684end
685
686--[[=============================================================================
687 ValidateControlMethod(controlMethod)
688
689 Description:
690 Identifies whether the specified control method has a valid connection
691
692 Parameters:
693 controlMethod(string) - The communication we are validating against
694 Valid types are (Network, Serial, and IR)
695
696 Returns:
697 true if the controlMethod specified has been connected, false otherwise.
698===============================================================================]]
699function ValidateControlMethod(controlMethod)
700 local isValid = false
701
702 if (controlMethod == "Network") and (gIsNetworkConnected) then
703 isValid = true
704 elseif (controlMethod == "URL") and (gIsUrlConnected) then
705 isValid = true
706 elseif (controlMethod == "Serial") and (gIsSerialConnected) then
707 isValid = true
708 elseif (controlMethod == "IR") and (gIsIRConnected) then
709 isValid = true
710 end
711
712 return isValid
713end
714 end)
715package.preload['common.c4_diagnostics'] = (function (...)
716--[[=============================================================================
717 Functions for Testing different aspects of the environment
718
719 Copyright 2016 Control4 Corporation. All Rights Reserved.
720===============================================================================]]
721require "common.c4_driver_declarations"
722
723-- Set template version for this file
724if (TEMPLATE_VERSION ~= nil) then
725 TEMPLATE_VERSION.c4_diagnostics = "2016.01.08"
726end
727
728function DisplayGlobals()
729
730 print ("Global Variables")
731 print ("----------------------------")
732 for k,v in pairs(_G) do -- globals
733 if not (type(v) == "function") then
734 if (string.find(k, "^g%L") == 1) then
735 print(k .. ": " .. tostring(v))
736 if (type(v) == "table") then
737 C4PrintTable(v, " ")
738 end
739 end
740 end
741 end
742
743 print ("")
744end
745
746function C4PrintTable(tValue, sIndent)
747
748 sIndent = sIndent or " "
749 for k,v in pairs(tValue) do
750
751 print(sIndent .. tostring(k) .. ": " .. tostring(v))
752 if (type(v) == "table") then
753 C4PrintTable(v, sIndent .. " ")
754 end
755 end
756end end)
757package.preload['common.c4_driver_declarations'] = (function (...)
758--[[=============================================================================
759 Driver Declarations used to call startup routines, teardown routines, and
760 other basic functions of the drivers operation
761
762 Copyright 2017 Control4 Corporation. All Rights Reserved.
763===============================================================================]]
764
765-- Template Version Table
766TEMPLATE_VERSION = {}
767TEMPLATE_VERSION.c4_driver_declarations = "2017.04.25"
768
769-- Command Handler Tables
770EX_CMD = {}
771PRX_CMD = {}
772UI_REQ = {}
773NOTIFY = {}
774DEV_MSG = {}
775LUA_ACTION = {}
776PROG_CONDITIONAL = {}
777
778
779--[[=============================================================================
780 Tables of functions
781 The following tables are function containers that are called within the
782 following functions:
783
784 OnDriverInit()
785 First calls all functions contained within ON_DRIVER_EARLY_INIT table
786 then calls all functions contained within ON_DRIVER_INIT table
787
788 OnDriverLateInit()
789 Calls all functions contained within ON_DRIVER_LATEINIT table
790
791 OnDriverDestroyed()
792 Calls all functions contained within ON_DRIVER_DESTROYED table
793
794 OnPropertyChanged()
795 Calls all functions contained within ON_PROPERTY_CHANGED table
796===============================================================================]]
797ON_DRIVER_INIT = {}
798ON_DRIVER_EARLY_INIT = {}
799ON_DRIVER_LATEINIT = {}
800ON_DRIVER_DESTROYED = {}
801ON_PROPERTY_CHANGED = {}
802
803-- Constants
804DEFAULT_PROXY_BINDINGID = 5001 end)
805package.preload['common.c4_init'] = (function (...)
806--[[=============================================================================
807 Initial driver initialization and destruction functions
808
809 Copyright 2016 Control4 Corporation. All Rights Reserved.
810===============================================================================]]
811require "common.c4_driver_declarations"
812require "common.c4_property"
813
814-- Set template version for this file
815if (TEMPLATE_VERSION ~= nil) then
816 TEMPLATE_VERSION.c4_init = "2016.01.08"
817end
818
819--[[=============================================================================
820 OnDriverInit()
821
822 Description
823 Invoked by director when a driver is loaded. This API is provided for the
824 driver developer to contain all of the driver objects that will require
825 initialization.
826
827 Parameters
828 None
829
830 Returns
831 Nothing
832===============================================================================]]
833function OnDriverInit()
834 gInitializingDriver = true
835 C4:ErrorLog("INIT_CODE: OnDriverInit()")
836
837 -- Call all ON_DRIVER_EARLY_INIT functions.
838 for k,v in pairs(ON_DRIVER_EARLY_INIT) do
839 if (ON_DRIVER_EARLY_INIT[k] ~= nil and type(ON_DRIVER_EARLY_INIT[k]) == "function") then
840 C4:ErrorLog("INIT_CODE: ON_DRIVER_EARLY_INIT." .. k .. "()")
841 local status, err = pcall(ON_DRIVER_EARLY_INIT[k])
842 if (not status) then
843 C4:ErrorLog("LUA_ERROR: " .. err)
844 end
845 end
846 end
847
848 -- Call all ON_DRIVER_INIT functions
849 for k,v in pairs(ON_DRIVER_INIT) do
850 if (ON_DRIVER_INIT[k] ~= nil and type(ON_DRIVER_INIT[k]) == "function") then
851 C4:ErrorLog("INIT_CODE: ON_DRIVER_INIT." .. k .. "()")
852 local status, err = pcall(ON_DRIVER_INIT[k])
853 if (not status) then
854 C4:ErrorLog("LUA_ERROR: " .. err)
855 end
856 end
857 end
858
859 -- Fire OnPropertyChanged to set the initial Headers and other Property
860 -- global sets, they'll change if Property is changed.
861 for k,v in pairs(Properties) do
862 C4:ErrorLog("INIT_CODE: Calling OnPropertyChanged - " .. k .. ": " .. v)
863 local status, err = pcall(OnPropertyChanged, k)
864 if (not status) then
865 C4:ErrorLog("LUA_ERROR: " .. err)
866 end
867 end
868
869 gInitializingDriver = false
870end
871
872--[[=============================================================================
873 OnDriverLateInit()
874
875 Description
876 Invoked by director after all drivers in the project have been loaded. This
877 API is provided for the driver developer to contain all of the driver
878 objects that will require initialization after all drivers in the project
879 have been loaded.
880
881 Parameters
882 None
883
884 Returns
885 Nothing
886===============================================================================]]
887function OnDriverLateInit()
888 C4:ErrorLog("INIT_CODE: OnDriverLateInit()")
889
890 -- Call all ON_DRIVER_LATEINIT functions
891 for k,v in pairs(ON_DRIVER_LATEINIT) do
892 if (ON_DRIVER_LATEINIT[k] ~= nil and type(ON_DRIVER_LATEINIT[k]) == "function") then
893 C4:ErrorLog("INIT_CODE: ON_DRIVER_LATEINIT." .. k .. "()")
894 ON_DRIVER_LATEINIT[k]()
895 end
896 end
897end
898
899
900--[[=============================================================================
901 OnDriverDestroyed()
902 Function called by Director when a driver is removed. Release things this
903 driver has allocated such as timers.
904
905 Parameters
906 None
907
908 Returns
909 Nothing
910===============================================================================]]
911function OnDriverDestroyed()
912 C4:ErrorLog("INIT_CODE: OnDriverDestroyed()")
913
914 -- Call all ON_DRIVER_DESTROYED functions
915 for k, v in pairs(ON_DRIVER_DESTROYED) do
916 if (ON_DRIVER_DESTROYED[k] ~= nil and type(ON_DRIVER_DESTROYED[k]) == "function") then
917 C4:ErrorLog("INIT_CODE: ON_DRIVER_DESTROYED." .. k .. "()")
918 ON_DRIVER_DESTROYED[k]()
919 end
920 end
921end end)
922package.preload['common.c4_ir_connection'] = (function (...)
923--[[=============================================================================
924 Base for an IR connection driver
925
926 Copyright 2016 Control4 Corporation. All Rights Reserved.
927===============================================================================]]
928require "common.c4_driver_declarations"
929require "common.c4_device_connection_base"
930require "lib.c4_log"
931require "common.c4_common"
932
933-- Set template version for this file
934if (TEMPLATE_VERSION ~= nil) then
935 TEMPLATE_VERSION.c4_ir_connection = "2016.01.08"
936end
937
938IRConnectionBase = inheritsFrom(DeviceConnectionBase)
939
940function IRConnectionBase:construct(BindingID)
941 self.superClass():construct()
942 self._BindingID = BindingID
943end
944
945function IRConnectionBase:Initialize(ExpectAck, DelayInterval, WaitInterval)
946 print("tSerConBase:Initialize")
947 gControlMethod = "IR"
948 self:superClass():Initialize(ExpectAck, DelayInterval, WaitInterval, self)
949end
950
951function IRConnectionBase:ControlMethod()
952 return "IR"
953end
954
955function IRConnectionBase:SendCommand(sCommand, ...)
956 if(self._IsConnected) then
957 local command_delay = select(1, ...)
958 local delay_units = select(2, ...)
959 local command_name = select(3, ...)
960
961 C4:SendIR(self._BindingID, sCommand)
962 self:StartCommandTimer(command_delay, delay_units, command_name)
963 else
964 LogWarn("IR connection is not bound. Command not sent.")
965 end
966end
967 end)
968package.preload['common.c4_networkserver_connection'] = (function (...)
969--[[=============================================================================
970 Base for a network server connection driver
971
972 Copyright 2016 Control4 Corporation. All Rights Reserved.
973===============================================================================]]
974require "common.c4_device_connection_base"
975require "lib.c4_log"
976
977-- Set template version for this file
978if (TEMPLATE_VERSION ~= nil) then
979 TEMPLATE_VERSION.c4_networkserver_connection = "2016.01.08"
980end
981
982DEFAULT_POLLING_INTERVAL_SECONDS = 30
983
984gNetworkKeepAliveInterval = DEFAULT_POLLING_INTERVAL_SECONDS
985
986JK_NETWORK_BINDING_ID = 6001
987JK_IP_ADDRESS = "192.168.0.169"
988JK_PORT = 0x0C00
989
990
991NetworkServerConnectionBase = inheritsFrom(DeviceConnectionBase)
992
993function NetworkServerConnectionBase:construct()
994 self.superClass():construct()
995
996 self._Port = JK_PORT
997 self._Handle = 0
998end
999
1000function NetworkServerConnectionBase:Initialize(ExpectAck, DelayInterval, WaitInterval)
1001 print("NetworkServerConnectionBase:Initialize")
1002 gControlMethod = "NetworkServer"
1003 self:superClass():Initialize(ExpectAck, DelayInterval, WaitInterval, self)
1004
1005end
1006
1007function NetworkServerConnectionBase:ControlMethod()
1008 return "NetworkServer"
1009end
1010
1011function NetworkServerConnectionBase:SendCommand(sCommand, ...)
1012 if(self._IsConnected) then
1013 if(self._IsOnline) then
1014 local command_delay = select(1, ...)
1015 local delay_units = select(2, ...)
1016 local command_name = select(3, ...)
1017
1018 C4:SendToNetwork(self._BindingID, self._Port, sCommand)
1019 self:StartCommandTimer(command_delay, delay_units, command_name)
1020 else
1021 self:CheckNetworkConnectionStatus()
1022 end
1023 else
1024 LogWarn("Not connected to network. Command not sent.")
1025 end
1026end
1027
1028
1029function NetworkServerConnectionBase:SendRaw(sData)
1030-- LogTrace("Sending raw: %s", HexToString(sData))
1031 C4:ServerSend(self._Handle, sData, #sData)
1032end
1033
1034
1035function NetworkServerConnectionBase:ReceivedFromNetworkServer(nHandle, sData)
1036 self._Handle = nHandle
1037 self:ReceivedFromCom(sData)
1038end
1039
1040
1041function NetworkServerConnectionBase:StartListening()
1042 LogTrace("Creating Listener on Port %d", self._Port)
1043 C4:CreateServer(self._Port)
1044end
1045
1046
1047function NetworkServerConnectionBase:StopListening()
1048 LogTrace("Closing Listener on Port %d", self._Port)
1049 C4:DestroyServer()
1050end
1051
1052
1053
1054-- function NetworkServerConnectionBase:CheckNetworkConnectionStatus()
1055 -- if (self._IsConnected and (not self._IsOnline)) then
1056 -- LogWarn("Network status is OFFLINE. Trying to reconnect to the device's Control port...")
1057 -- C4:NetDisconnect(self._BindingID, self._Port)
1058 -- C4:NetConnect(self._BindingID, self._Port)
1059 -- end
1060-- end
1061
1062-- function NetworkServerConnectionBase.OnKeepAliveTimerExpired(Instance)
1063 -- Instance._LastCheckin = Instance._LastCheckin + 1
1064
1065 -- if(Instance._LastCheckin > 2) then
1066 -- if(not Instance._IsOnline) then
1067 -- C4:NetDisconnect(Instance._BindingID, Instance._Port)
1068 -- C4:NetConnect(Instance._BindingID, Instance._Port)
1069 -- else
1070 -- C4:NetDisconnect(Instance._BindingID, Instance._Port)
1071 -- LogWarn("Failed to receive poll responses... Disconnecting...")
1072 -- end
1073 -- end
1074
1075 -- if (SendKeepAlivePollingCommand ~= nil and type(SendKeepAlivePollingCommand) == "function") then
1076 -- SendKeepAlivePollingCommand()
1077 -- end
1078
1079 -- Instance._KeepAliveTimer:StartTimer(gNetworkKeepAliveInterval)
1080-- end
1081
1082-- function NetworkServerConnectionBase:SetOnlineStatus(IsOnline)
1083 -- self._IsOnline = IsOnline
1084
1085 -- if(IsOnline) then
1086 -- self._KeepAliveTimer:StartTimer()
1087 -- self._LastCheckin = 0
1088 -- if (UpdateProperty ~= nil and type(UpdateProperty) == "function") then
1089 -- UpdateProperty("Connected To Network", "true")
1090 -- end
1091
1092 -- self:SendNextCommand()
1093 -- else
1094 -- self._KeepAliveTimer:KillTimer()
1095 -- if (UpdateProperty ~= nil and type(UpdateProperty) == "function") then
1096 -- UpdateProperty("Connected To Network", "false")
1097 -- end
1098 -- end
1099-- end
1100
1101 end)
1102package.preload['common.c4_network_connection'] = (function (...)
1103--[[=============================================================================
1104 Base for a network connection driver
1105
1106 Copyright 2016 Control4 Corporation. All Rights Reserved.
1107===============================================================================]]
1108require "common.c4_device_connection_base"
1109require "lib.c4_log"
1110
1111-- Set template version for this file
1112if (TEMPLATE_VERSION ~= nil) then
1113 TEMPLATE_VERSION.c4_network_connection = "2016.01.08"
1114end
1115
1116DEFAULT_POLLING_INTERVAL_SECONDS = 30
1117DEFAULT_RECONNECT_INTERVAL_SECONDS = 5
1118
1119gNetworkKeepAliveInterval = DEFAULT_POLLING_INTERVAL_SECONDS
1120gNetworkReconnectInterval = DEFAULT_RECONNECT_INTERVAL_SECONDS
1121
1122NetworkConnectionBase = inheritsFrom(DeviceConnectionBase)
1123
1124function NetworkConnectionBase:construct(BindingID, Port)
1125 self.superClass():construct()
1126
1127 self._BindingID = BindingID
1128 self._Port = Port
1129 self._LastCheckin = 0
1130 self._IsOnline = false
1131 self._KeepAliveTimer = nil
1132end
1133
1134function NetworkConnectionBase:Initialize(ExpectAck, DelayInterval, WaitInterval)
1135 print("NetConBase:Initialize")
1136 gControlMethod = "Network"
1137 self:superClass():Initialize(ExpectAck, DelayInterval, WaitInterval, self)
1138 self._KeepAliveTimer = c4_timer:new("PollingTimer", gNetworkKeepAliveInterval, "SECONDS", NetworkConnectionBase.OnKeepAliveTimerExpired, false, self)
1139 self._NetworkReconnectTimer = c4_timer:new("NetworkReconnectTimer", gNetworkReconnectInterval, "SECONDS", NetworkConnectionBase.OnNetworkReconnectTimerExpired, false, self)
1140end
1141
1142function NetworkConnectionBase:ControlMethod()
1143 return "Network"
1144end
1145
1146function NetworkConnectionBase:SendCommand(sCommand, ...)
1147 if(self._IsConnected) then
1148 if(self._IsOnline) then
1149 local command_delay = select(1, ...)
1150 local delay_units = select(2, ...)
1151 local command_name = select(3, ...)
1152
1153 C4:SendToNetwork(self._BindingID, self._Port, sCommand)
1154 self:StartCommandTimer(command_delay, delay_units, command_name)
1155 else
1156 self:CheckNetworkConnectionStatus()
1157 end
1158 else
1159 LogWarn("Not connected to network. Command not sent.")
1160 end
1161end
1162
1163function NetworkConnectionBase:ReceivedFromNetwork(idBinding, nPort, sData)
1164 self._LastCheckin = 0
1165 self:ReceivedFromCom(sData)
1166end
1167
1168function NetworkConnectionBase:CheckNetworkConnectionStatus()
1169 if (self._IsConnected and (not self._IsOnline)) then
1170 LogWarn("Network status is OFFLINE. Trying to reconnect to the device's Control port...")
1171 C4:NetDisconnect(self._BindingID, self._Port)
1172 --C4:NetConnect(self._BindingID, self._Port)
1173 self._NetworkReconnectTimer:StartTimer(gNetworkReconnectInterval)
1174 end
1175end
1176
1177function NetworkConnectionBase.OnKeepAliveTimerExpired(Instance)
1178 Instance._LastCheckin = Instance._LastCheckin + 1
1179
1180 if(Instance._LastCheckin == 3) then
1181 LogWarn("Failed to receive poll responses... initiating network recovery mode...")
1182 C4:NetDisconnect(Instance._BindingID, Instance._Port)
1183 Instance._NetworkReconnectTimer:StartTimer(gNetworkReconnectInterval)
1184 return
1185 elseif(Instance._LastCheckin > 4) then
1186 Instance._LastCheckin = 4
1187 end
1188
1189 if (SendKeepAlivePollingCommand ~= nil and type(SendKeepAlivePollingCommand) == "function" and Instance._IsOnline) then
1190 SendKeepAlivePollingCommand()
1191 end
1192
1193 Instance._KeepAliveTimer:StartTimer(gNetworkKeepAliveInterval)
1194end
1195
1196function NetworkConnectionBase.OnNetworkReconnectTimerExpired(Instance)
1197 if (Instance._IsConnected) then
1198 LogWarn("OnNetworkReconnectTimerExpired: Attempting to reactivate network connection...")
1199 C4:NetDisconnect(Instance._BindingID, Instance._Port)
1200 C4:NetConnect(Instance._BindingID, Instance._Port)
1201 Instance._NetworkReconnectTimer:StartTimer(gNetworkReconnectInterval)
1202 else
1203 LogWarn("Cannot attempt to reactivate, the network connection is not bound")
1204 end
1205end
1206
1207function NetworkConnectionBase:SetOnlineStatus(IsOnline)
1208 self._IsOnline = IsOnline
1209
1210 if(IsOnline) then
1211 self._KeepAliveTimer:StartTimer(gNetworkKeepAliveInterval)
1212 self._NetworkReconnectTimer:KillTimer()
1213 self._LastCheckin = 0
1214 if (UpdateProperty ~= nil and type(UpdateProperty) == "function") then
1215 UpdateProperty("Connected To Network", "true")
1216 end
1217
1218 self:SendNextCommand()
1219 else
1220 self._KeepAliveTimer:KillTimer()
1221 self._NetworkReconnectTimer:StartTimer(gNetworkReconnectInterval)
1222 if (UpdateProperty ~= nil and type(UpdateProperty) == "function") then
1223 UpdateProperty("Connected To Network", "false")
1224 end
1225 end
1226end
1227
1228function ON_DRIVER_LATEINIT.c4_network_connection()
1229 -- Ensure existing connection is taken into consideration (useful on Driver Update)
1230 if (gIsNetworkConnected) then
1231 if (gCon ~= nil and gCon._BindingID ~= nil) then
1232 local tmp = C4:GetBindingAddress(gCon._BindingID)
1233 if (tmp ~= nil and string.len(tmp) > 0) then
1234 OnNetworkBindingChanged(gCon._BindingID, true)
1235 end
1236 end
1237 end
1238end
1239 end)
1240package.preload['common.c4_notify'] = (function (...)
1241--[[=============================================================================
1242 Notification Functions
1243
1244 Copyright 2016 Control4 Corporation. All Rights Reserved.
1245===============================================================================]]
1246require "common.c4_driver_declarations"
1247
1248-- Set template version for this file
1249if (TEMPLATE_VERSION ~= nil) then
1250 TEMPLATE_VERSION.c4_notify = "2016.01.08"
1251end
1252
1253--[[=============================================================================
1254 SendNotify(notifyText, tParams, bindingID)
1255
1256 Description
1257 Forwards a notification to the proxy with a list of parameters
1258
1259 Parameters
1260 notifyText(string) - The function identifier for the proxy
1261 tParams(table) - Table of key value pairs that hold the the parameters
1262 and their values used in the proxy function
1263 bindingID(int) - The requests binding id
1264
1265 Returns
1266 Nothing
1267===============================================================================]]
1268function SendNotify(notifyText, tParams, bindingID)
1269 C4:SendToProxy(bindingID, notifyText, tParams, "NOTIFY")
1270end
1271
1272--[[=============================================================================
1273 SendSimpleNotify(notifyText, ...)
1274
1275 Description
1276 Forwards a notification to the proxy with no parameters
1277
1278 Parameters
1279 notifyText(string) - The function identifier for the proxy
1280 bindingID(int) - Optional parameter containing the requests binding id,
1281 if not specified then the DEFAULT_PROXY_ID is given.
1282
1283 Returns
1284 Nothing
1285===============================================================================]]
1286function SendSimpleNotify(notifyText, ...)
1287 bindingID = select(1, ...) or DEFAULT_PROXY_BINDINGID
1288 C4:SendToProxy(bindingID, notifyText, {}, "NOTIFY")
1289end end)
1290package.preload['common.c4_property'] = (function (...)
1291--[[=============================================================================
1292 Function for changing properties
1293
1294 Copyright 2016 Control4 Corporation. All Rights Reserved.
1295===============================================================================]]
1296require "common.c4_driver_declarations"
1297
1298-- Set template version for this file
1299if (TEMPLATE_VERSION ~= nil) then
1300 TEMPLATE_VERSION.c4_property = "2016.01.08"
1301end
1302
1303--[[=============================================================================
1304 OnPropertyChanged(sProperty)
1305
1306 Description
1307 Function called by Director when a property changes value. The value of the
1308 property that has changed can be found with: Properties[sName]. Note that
1309 OnPropertyChanged is not called when the Property has been changed by the
1310 driver calling the UpdateProperty command, only when the Property is changed
1311 by the user from the Properties Page. This function is called by Director
1312 when a property changes value.
1313
1314 Parameters
1315 sProperty(string) - Name of property that has changed.
1316
1317 Returns
1318 Nothing
1319===============================================================================]]
1320function OnPropertyChanged(sProperty)
1321 local propertyValue = Properties[sProperty]
1322
1323 if (LOG ~= nil and type(LOG) == "table") then
1324 LogTrace("OnPropertyChanged(" .. sProperty .. ") changed to: " .. Properties[sProperty])
1325 end
1326
1327 -- Remove any spaces (trim the property)
1328 local trimmedProperty = string.gsub(sProperty, " ", "")
1329 local status = true
1330 local err = ""
1331
1332 if (ON_PROPERTY_CHANGED[sProperty] ~= nil and type(ON_PROPERTY_CHANGED[sProperty]) == "function") then
1333 status, err = pcall(ON_PROPERTY_CHANGED[sProperty], propertyValue)
1334 elseif (ON_PROPERTY_CHANGED[trimmedProperty] ~= nil and type(ON_PROPERTY_CHANGED[trimmedProperty]) == "function") then
1335 status, err = pcall(ON_PROPERTY_CHANGED[trimmedProperty], propertyValue)
1336 end
1337
1338 if (not status) then
1339 LogError("LUA_ERROR: " .. err)
1340 end
1341end
1342
1343--[[=============================================================================
1344 UpdateProperty(propertyName, propertyValue)
1345
1346 Description:
1347 Sets the value of the given property in the driver
1348
1349 Parameters:
1350 propertyName(string) - The name of the property to change
1351 propertyValue(string) - The value of the property being changed
1352
1353 Returns:
1354 None
1355===============================================================================]]
1356function UpdateProperty(propertyName, propertyValue)
1357 if (Properties[propertyName] ~= nil) then
1358 C4:UpdateProperty(propertyName, propertyValue)
1359 end
1360end
1361 end)
1362package.preload['common.c4_serial_connection'] = (function (...)
1363--[[=============================================================================
1364 Base for a serial connection driver
1365
1366 Copyright 2016 Control4 Corporation. All Rights Reserved.
1367===============================================================================]]
1368require "common.c4_driver_declarations"
1369require "common.c4_device_connection_base"
1370require "lib.c4_log"
1371require "common.c4_common"
1372
1373-- Set template version for this file
1374if (TEMPLATE_VERSION ~= nil) then
1375 TEMPLATE_VERSION.c4_serial_connection = "2016.01.08"
1376end
1377
1378SerialConnectionBase = inheritsFrom(DeviceConnectionBase)
1379
1380function SerialConnectionBase:construct(BindingID)
1381 self.superClass():construct()
1382 self._BindingID = BindingID
1383end
1384
1385function SerialConnectionBase:Initialize(ExpectAck, DelayInterval, WaitInterval)
1386 gControlMethod = "Serial"
1387 self:superClass():Initialize(ExpectAck, DelayInterval, WaitInterval, self)
1388end
1389
1390function SerialConnectionBase:ControlMethod()
1391 return "Serial"
1392end
1393
1394function SerialConnectionBase:SendCommand(sCommand, ...)
1395 if(self._IsConnected) then
1396 local command_delay = select(1, ...)
1397 local delay_units = select(2, ...)
1398 local command_name = select(3, ...)
1399
1400 C4:SendToSerial(self._BindingID, sCommand)
1401 self:StartCommandTimer(command_delay, delay_units, command_name)
1402 else
1403 LogWarn("Not connected to serial. Command not sent.")
1404 end
1405end
1406
1407function SerialConnectionBase:SendRaw(sData)
1408 C4:SendToSerial(self._BindingID, sData)
1409end
1410
1411
1412function SerialConnectionBase:ReceivedFromSerial(idBinding, sData)
1413 self:ReceivedFromCom(sData)
1414end
1415 end)
1416package.preload['common.c4_url_connection'] = (function (...)
1417--[[=============================================================================
1418 Base for a url connection driver
1419
1420 Copyright 2016 Control4 Corporation. All Rights Reserved.
1421===============================================================================]]
1422require "common.c4_device_connection_base"
1423
1424-- Set template version for this file
1425if (TEMPLATE_VERSION ~= nil) then
1426 TEMPLATE_VERSION.c4_url_connection = "2016.01.08"
1427end
1428
1429UrlConnectionBase = inheritsFrom(DeviceConnectionBase)
1430
1431function UrlConnectionBase:construct(Url)
1432 self.superClass():construct()
1433 self._Url = Url
1434end
1435
1436function UrlConnectionBase:Initialize(ExpectAck, DelayInterval, WaitInterval)
1437 gControlMethod = "URL"
1438 self:superClass():Initialize(ExpectAck, DelayInterval, WaitInterval, self)
1439 OnURLConnectionChanged()
1440end
1441
1442function UrlConnectionBase:ControlMethod()
1443 return "URL"
1444end
1445
1446function UrlConnectionBase:SetUrl(Url)
1447 self._Url = Url
1448end
1449
1450function UrlConnectionBase:SendCommand(sCommand, sHeader, ignoreConnect)
1451 ignoreConnect = ignoreConnect or false
1452
1453 local ticketId
1454 if(self._IsConnected or ignoreConnect) then
1455 if (sHeader ~= nil) then
1456 ticketId = C4:urlPost(self._Url, sCommand, sHeader)
1457 else
1458 ticketId = C4:urlPost(self._Url, sCommand)
1459 end
1460 else
1461 LogWarn("Not connected. Command not sent.")
1462 end
1463
1464 return ticketId
1465end
1466
1467function UrlConnectionBase:SendCommandUrl(sCommand, url, sHeader, ignoreConnect)
1468 ignoreConnect = ignoreConnect or false
1469
1470 local ticketId
1471 if(self._IsConnected or ignoreConnect) then
1472 if (sHeader ~= nil) then
1473 ticketId = C4:urlPost(url, sCommand, sHeader)
1474 else
1475 ticketId = C4:urlPost(url, sCommand)
1476 end
1477 else
1478 LogWarn("Not connected. Command not sent.")
1479 end
1480
1481 return ticketId
1482end
1483
1484function UrlConnectionBase:UrlPost(sCommand, url, sHeader, ignoreConnect)
1485 ignoreConnect = ignoreConnect or false
1486
1487 local ticketId
1488 if(self._IsConnected or ignoreConnect) then
1489 if (sHeader ~= nil) then
1490 ticketId = C4:urlPost(url, sCommand, sHeader)
1491 else
1492 ticketId = C4:urlPost(url, sCommand)
1493 end
1494 else
1495 LogWarn("Not connected. Command not sent.")
1496 end
1497
1498 return ticketId
1499end
1500
1501function UrlConnectionBase:UrlGet(url, sHeader, ignoreConnect)
1502 ignoreConnect = ignoreConnect or false
1503
1504 local ticketId
1505 if(self._IsConnected or ignoreConnect) then
1506 if (sHeader ~= nil) then
1507 ticketId = C4:urlGet(url, sHeader)
1508 else
1509 ticketId = C4:urlGet(url)
1510 end
1511 else
1512 LogWarn("Not connected. Command not sent.")
1513 end
1514
1515 return ticketId
1516end
1517
1518function UrlConnectionBase:ReceivedAsync(ticketId, sData, responseCode, tHeaders)
1519 LogTrace("ReceivedAsync[" .. ticketId .. "]: Response Code: " .. responseCode .. " Length: " .. string.len(sData))
1520 LogTrace(tHeaders)
1521 local tMessage = {
1522 ["ticketId"] = ticketId,
1523 ["sData"] = sData,
1524 ["responseCode"] = responseCode,
1525 ["tHeaders"] = tHeaders
1526 }
1527
1528 status, err = pcall(HandleMessage, tMessage)
1529 if (not status) then
1530 LogError("LUA_ERROR: " .. err)
1531 end
1532end
1533
1534function ConnectURL()
1535 gIsUrlConnected = true
1536 SetControlMethod()
1537end
1538
1539function DisconnectURL()
1540 gIsUrlConnected = false
1541 SetControlMethod()
1542end
1543 end)
1544package.preload['common.c4_utils'] = (function (...)
1545--[[=============================================================================
1546 Helper functions
1547
1548 Copyright 2016 Control4 Corporation. All Rights Reserved.
1549===============================================================================]]
1550
1551-- Set template version for this file
1552if (TEMPLATE_VERSION ~= nil) then
1553 TEMPLATE_VERSION.c4_utils = "2016.01.08"
1554end
1555
1556--[[=============================================================================
1557 AsciiToBCD(InString)
1558
1559 Description
1560 Convert an ascii string to a binary coded decimal. Each decimal digit is
1561 stored in one byte, with the lower four bits encoding the digit in BCD form.
1562
1563 Parameters
1564 InString(string) - Ascii string that is to be converted into bcd
1565
1566 Returns
1567 The binary coded decimal
1568===============================================================================]]
1569function AsciiToBCD(InString)
1570 local WorkVal = 0
1571 local RetValStr = ""
1572 local DoingHighNybble = false
1573 local WorkStr = ((#InString % 2) == 0) and (InString) or ("0" .. InString) -- make sure length is an even number
1574
1575 for CharCount = 1, #WorkStr do
1576 local NumVal = tonumber(WorkStr:sub(CharCount, CharCount))
1577
1578 WorkVal = bit.lshift(WorkVal, 4) + NumVal
1579 if (DoingHighNybble) then
1580 RetValStr = RetValStr .. string.char(WorkVal)
1581 WorkVal = 0
1582 end
1583
1584 DoingHighNybble = (not DoingHighNybble)
1585 end
1586
1587 return RetValStr
1588end
1589
1590--[[=============================================================================
1591 BCDToAscii(InByte)
1592
1593 Description
1594 Convert an BCD string to an ascii string.
1595
1596 Parameters
1597 InByte(string) - Binary coded decimal that is to be converted into ascii
1598
1599 Returns
1600 The ascii string
1601===============================================================================]]
1602function BCDToAscii(InByte)
1603 return tostring(bit.rshift(InByte, 4)) .. tostring(bit.band(InByte, 0x0F))
1604end
1605
1606--[[=============================================================================
1607 MakeXMLNode(Tag, Value)
1608
1609 Description
1610 Create an Xml element
1611
1612 Parameters
1613 Tag(string) - The Xml elements name
1614 Value(string) - The Xml elements value
1615
1616 Returns
1617 The xml element created for the specified value
1618===============================================================================]]
1619function MakeXMLNode(Tag, Value)
1620 return "<" .. Tag .. ">" .. Value .. "</" .. Tag .. ">"
1621end
1622
1623--[[=============================================================================
1624 MakeXMLAttrNode(Tag, Value, Attribute, AttrValue)
1625
1626 Description
1627 Create an Xml element with an attribute
1628
1629 Parameters
1630 Tag(string) - The Xml elements name
1631 Value(string) - The Xml elements value
1632 Attribute(string) - The attribute to be added to the Xml element
1633 AttrValue(string) - The value of the attribute to be added
1634
1635 Returns
1636 The xml element created for the specified value
1637===============================================================================]]
1638function MakeXMLAttrNode(Tag, Value, Attribute, AttrValue)
1639 return "<" .. Tag .. " " .. Attribute .. "=\"" .. AttrValue .. "\">" .. Value .. "</" .. Tag .. ">"
1640end
1641
1642--[[=============================================================================
1643 StringFromUnicode(UnicodeString)
1644
1645 Description
1646 Convert a unicode string
1647
1648 Parameters
1649 UnicodeString(string) - The unicode string to be converted to ascii
1650
1651 Returns
1652 The ascii representation of the unicode string
1653===============================================================================]]
1654function StringFromUnicode(UnicodeString)
1655 local RetVal = ""
1656
1657 -- extract every other byte from the unicode string
1658 for Index = 2, #UnicodeString, 2 do
1659 RetVal = RetVal .. string.sub(UnicodeString, Index, Index)
1660 end
1661
1662 return RetVal
1663end
1664
1665--[[=============================================================================
1666 StringSplit(s)
1667
1668 Description
1669 Splits a string into multiple strings at an optionally specified delimiter
1670 If the delimiter is not specified, it will defalt to the space character
1671
1672 Parameters
1673 s(string) - The string that is to be split into several strings
1674 d(string) - The delimiter to split the string on
1675
1676 Returns
1677 A table of strings containing all the seperate values in the given string
1678===============================================================================]]
1679function StringSplit(s, d)
1680 local delim = (d ~= nil) and d or " "
1681 local result = {}
1682
1683 if s == nil or s == "" then
1684 return result
1685 end
1686
1687 for match in (s..delim):gmatch("(.-)"..delim) do
1688 table.insert(result, match)
1689 end
1690
1691 return result
1692end
1693
1694--[[=============================================================================
1695 toboolean(s)
1696
1697 Description
1698 Returns a boolean representation of the given value
1699
1700 Parameters
1701 val input value, may be of different types
1702
1703 Returns
1704 The value true or false based on the given value
1705 If the value is of type string the return true if the first letter is "T" or "t" or if the string is "1"
1706 If the value is of type number the return true if the value is non-zero
1707 If the value is already a boolean, just return it.
1708===============================================================================]]
1709function toboolean(val)
1710 local rval = false;
1711
1712 if type(val) == "string" and (string.lower(val) == 'true' or val == "1") then
1713 rval = true
1714 elseif type(val) == "number" and val ~= 0 then
1715 rval = true
1716 elseif type(val) == "boolean" then
1717 rval = val
1718 end
1719
1720 return rval
1721end
1722
1723--[[=============================================================================
1724 tointeger(s)
1725
1726 Description
1727 Force a number or a string representation of a number to be an integer
1728
1729 Parameters
1730 val - A number or a string representation of a number
1731
1732 Returns
1733 The the rounded off integer value.
1734===============================================================================]]
1735function tointeger(val)
1736 local nval = tonumber(val)
1737 return (nval >= 0) and math.floor(nval + 0.5) or math.ceil(nval - 0.5)
1738end
1739
1740
1741--[[=============================================================================
1742 Go(to, err, ...)
1743
1744 Description
1745 Call a function with the given arguments if it exists or report the error
1746
1747 Parameters
1748 to(string) - The string to evaluate the boolean representation from
1749 err(string) - The error to report if the function does not exist
1750 ... - Additional optional parameters for the function specified by
1751 the "to" parameter
1752
1753 Returns
1754 Nothing
1755===============================================================================]]
1756function Go(to, err, ...)
1757 if (type(to) == "function") then
1758 return to(...)
1759 else
1760 LogTrace(err)
1761 end
1762end
1763
1764--[[=============================================================================
1765 IsEmpty(str)
1766
1767 Description
1768 Identifies if the string given is nil or empty
1769
1770 Parameters
1771 str(string) - The string to evaluate for the empty condition
1772
1773 Returns
1774 True if the given value is empty, false otherwise
1775===============================================================================]]
1776function IsEmpty(str)
1777 return str == nil or str == ""
1778end
1779
1780--[[=============================================================================
1781 ReverseTable(a)
1782
1783 Description
1784 Reverse table entries (key=value, value=key)
1785
1786 Parameters
1787 a(table) - The table to reverse
1788
1789 Returns
1790 new reversed table
1791===============================================================================]]
1792function ReverseTable(a)
1793 local b = {}
1794 for k,v in pairs(a) do b[v] = k end
1795 return b
1796end
1797
1798function tonumber_loc(str, base)
1799 local s = str:gsub(",", ".") -- Assume US Locale decimal separator
1800 local num = tonumber(s, base)
1801 if (num == nil) then
1802 s = str:gsub("%.", ",") -- Non-US Locale decimal separator
1803 num = tonumber(s, base)
1804 end
1805 return num
1806end
1807
1808--[[=============================================================================
1809 HexToString(InString)
1810
1811 Description
1812 Converts a string of Hex characters to a readable string of ASCII characters
1813
1814 Parameters
1815 InString(string) - The string to be converted
1816
1817 Returns
1818 A string showing the hex bytes of the InString
1819===============================================================================]]
1820function HexToString(InString)
1821 local RetVal = ""
1822
1823 for Index = 1, #InString do
1824 RetVal = RetVal .. string.format("%02X ", InString:byte(Index))
1825 end
1826 return RetVal
1827end
1828
1829
1830--[[=============================================================================
1831 StringToHex(InString)
1832
1833 Description
1834 Converts a string of ASCII characters to as string with the actual Hex bytes in them.
1835 Basically an array of hex bytes.
1836
1837 Parameters
1838 InString(string) - The string to be converted
1839
1840 Returns
1841 A string of hex bytes (really an array of hex values)
1842===============================================================================]]
1843function StringToHex(InString)
1844 local RetVal = ""
1845
1846 for HexByteString in string.gfind(InString, "%x%x") do
1847 RetVal = RetVal .. string.char(tonumber(HexByteString, 16))
1848 end
1849 return RetVal
1850end
1851
1852function RecordHistory(severity, eventType, category, subcategory, description)
1853 C4:RecordHistory(severity, eventType, category, subcategory, description)
1854end
1855
1856function RecordCriticalHistory(eventType, category, subcategory, description)
1857 RecordHistory("Critical", eventType, category, subcategory, description)
1858end
1859
1860function RecordWarningHistory(eventType, category, subcategory, description)
1861 RecordHistory("Warning", eventType, category, subcategory, description)
1862end
1863
1864function RecordInfoHistory(eventType, category, subcategory, description)
1865 RecordHistory("Info", eventType, category, subcategory, description)
1866end
1867
1868
1869 end)
1870package.preload['lib.c4_log'] = (function (...)
1871--[[=============================================================================
1872 c4_log Class
1873
1874 Copyright 2016 Control4 Corporation. All Rights Reserved.
1875===============================================================================]]
1876require "common.c4_driver_declarations"
1877require "lib.c4_object"
1878
1879-- Set template version for this file
1880if (TEMPLATE_VERSION ~= nil) then
1881 TEMPLATE_VERSION.c4_log = "2016.01.08"
1882end
1883
1884c4_log = inheritsFrom(nil)
1885
1886function c4_log:construct(logName)
1887 self._logLevel = tonumber(string.sub(Properties['Log Level'] or "", 1, 1)) or 5
1888 self._outputPrint = Properties['Log Mode']:find("Print") ~= nil
1889 self._outputC4Log = Properties['Log Mode']:find("Log") ~= nil
1890 self._logName = logName or ""
1891
1892 -- make sure Property is up to date (no harm if absent)
1893 C4:UpdateProperty("Log Level", Properties['Log Level'])
1894end
1895
1896function c4_log:SetLogLevel(level)
1897 self._logLevel = tonumber(string.sub(level or "", 1, 1)) or self._logLevel
1898end
1899
1900function c4_log:LogLevel()
1901 return self._logLevel
1902end
1903
1904function c4_log:OutputPrint(value)
1905 self._outputPrint = value
1906end
1907
1908function c4_log:OutputC4Log(value)
1909 self._outputC4Log = value
1910end
1911
1912function c4_log:SetLogName(logName)
1913
1914 if (logName == nil or logName == "") then
1915 logName = ""
1916 else
1917 logName = logName .. ": "
1918 end
1919
1920 self._logName = logName
1921end
1922
1923function c4_log:LogName()
1924 return self._logName
1925end
1926
1927function c4_log:Enabled()
1928 return (self._outputPrint or self._outputC4Log)
1929end
1930
1931function c4_log:PrintEnabled()
1932 return self._outputPrint
1933end
1934
1935function c4_log:C4LogEnabled()
1936 return self._outputC4Log
1937end
1938
1939function c4_log:CreateTableText(tValue, tableText)
1940 tableText = tableText or ""
1941
1942 if (type(tValue) == "table") then
1943
1944 tableText = tableText .. "{"
1945 for k, v in pairs(tValue) do
1946
1947 -- add key
1948 if (type(k) == "number") then
1949 tableText = tableText .. "[" .. tostring(k) .. "]="
1950 elseif (type(k) == "string") then
1951 tableText = tableText .. k .. "="
1952 else
1953 print (tostring(k) .. ": " .. tostring (v))
1954 end
1955
1956 -- add value
1957 if (type(v) == "number") then
1958 tableText = tableText .. tostring(v) .. ","
1959 elseif (type(v) == "string") then
1960 tableText = tableText .. "'" .. v .. "',"
1961 elseif (type(v) == "table") then
1962 tableText = c4_log:CreateTableText(v, tableText)
1963 tableText = tableText .. ","
1964 elseif (type(v) == "boolean") then
1965 tableText = tableText .. tostring(v) .. ","
1966 end
1967 end
1968
1969 tableText = tableText .. "}"
1970 end
1971
1972 return tableText
1973end
1974
1975function InsertIndent(indentLevel)
1976 local indentStr = ""
1977
1978 for i=1, indentLevel do
1979 indentStr = indentStr .. "\t"
1980 end
1981
1982 return indentStr
1983end
1984
1985function c4_log:CreateTableTextFormatted(tValue, tableText, indentLevel)
1986 tableText = tableText or ""
1987 indentLevel = indentLevel or 0
1988
1989 if (type(tValue) == "table") then
1990
1991 indentLevel = indentLevel + 1
1992 tableText = tableText .. "{\n"
1993 for k, v in pairs(tValue) do
1994
1995 -- add key
1996 if (type(k) == "number") then
1997 tableText = tableText .. InsertIndent(indentLevel) .. "[" .. tostring(k) .. "]="
1998 elseif (type(k) == "string") then
1999 tableText = tableText .. InsertIndent(indentLevel) .. k .. "="
2000 else
2001 print (tostring(k) .. ": " .. tostring (v))
2002 end
2003
2004 -- add value
2005 if (type(v) == "number") then
2006 tableText = tableText .. tostring(v) .. ",\n"
2007 elseif (type(v) == "string") then
2008 tableText = tableText .. "'" .. v .. "',\n"
2009 elseif (type(v) == "table") then
2010 tableText = c4_log:CreateTableTextFormatted(v, tableText, indentLevel)
2011 tableText = tableText .. ",\n"
2012 elseif (type(v) == "boolean") then
2013 tableText = tableText .. tostring(v) .. ",\n"
2014 end
2015 end
2016
2017 indentLevel = indentLevel - 1
2018 tableText = tableText .. InsertIndent(indentLevel) .. "}"
2019 end
2020
2021 return tableText
2022end
2023
2024MAX_TABLE_LEVELS = 10
2025function c4_log:PrintTable(tValue, tableText, sIndent, level)
2026 tableText = tableText or ""
2027 level = level + 1
2028
2029 if (level <= MAX_TABLE_LEVELS) then
2030 if (type(tValue) == "table") then
2031 for k,v in pairs(tValue) do
2032 if (tableText == "") then
2033 tableText = sIndent .. tostring(k) .. ": " .. tostring(v)
2034 if (sIndent == ". ") then sIndent = " " end
2035 else
2036 tableText = tableText .. "\n" .. sIndent .. tostring(k) .. ": " .. tostring(v)
2037 end
2038 if (type(v) == "table") then
2039 tableText = self:PrintTable(v, tableText, sIndent .. " ", level)
2040 end
2041 end
2042 else
2043 tableText = tableText .. "\n" .. sIndent .. tostring(tValue)
2044 end
2045 end
2046
2047 return tableText
2048end
2049
2050function c4_log:LogTable(tValue, sIndent, level)
2051 level = level + 1
2052
2053 if (level <= MAX_TABLE_LEVELS) then
2054 if (type(tValue) == "table") then
2055 for k,v in pairs(tValue) do
2056 C4:ErrorLog(self._logName .. sIndent .. tostring(k) .. ": " .. tostring(v))
2057 if (type(v) == "table") then
2058 self:LogTable(v, sIndent .. " ", level)
2059 end
2060 end
2061 else
2062 C4:ErrorLog(self._logName .. sIndent .. tValue)
2063 end
2064 end
2065end
2066
2067function c4_log:Print(logLevel, sLogText)
2068
2069 if (self._logLevel >= logLevel) then
2070 if (type(sLogText) == "table") then
2071 if (self._outputPrint) then
2072 print (self:PrintTable(sLogText, tableText, ". ", 0))
2073 end
2074
2075 if (self._outputC4Log) then
2076 self:LogTable(sLogText, " ", 0)
2077 end
2078
2079 return
2080 end
2081
2082 if (self._outputPrint) then
2083 print (sLogText)
2084 end
2085
2086 if (self._outputC4Log) then
2087 C4:ErrorLog(self._logName .. tostring(sLogText))
2088 end
2089 end
2090end
2091
2092function c4_log:Fatal(sLogText, ...)
2093 self:LogOutput(0, sLogText, ...)
2094end
2095
2096function c4_log:Error(sLogText, ...)
2097 self:LogOutput(1, sLogText, ...)
2098end
2099
2100function c4_log:Warn(sLogText, ...)
2101 self:LogOutput(2, sLogText, ...)
2102end
2103
2104function c4_log:Info(sLogText, ...)
2105 self:LogOutput(3, sLogText, ...)
2106end
2107
2108function c4_log:Debug(sLogText, ...)
2109 self:LogOutput(4, sLogText, ...)
2110end
2111
2112function c4_log:Trace(sLogText, ...)
2113 self:LogOutput(5, sLogText, ...)
2114end
2115
2116function c4_log:LogOutput(level, sLogText, ...)
2117 if (LogEnabled()) then
2118 if (type(sLogText) == "string") then
2119 sLogText = string.format(sLogText, ...)
2120 end
2121
2122 self:Print(level, sLogText)
2123 end
2124end
2125
2126--[[=============================================================================
2127 c4_log wrapper functions
2128===============================================================================]]
2129function TryLog(level, sLogText, ...)
2130 LOG:LogOutput(level, sLogText, ...)
2131end
2132
2133--[[=============================================================================
2134 SetLogLevel(level)
2135
2136 Description:
2137 Sets the desired log level to view
2138
2139 Parameters:
2140 level(int) - The logging level to set the message to
2141 0 = Fatal
2142 1 = Error
2143 2 = Warn
2144 3 = Info
2145 4 = Debug
2146 5 = Trace
2147
2148 Returns:
2149 None
2150===============================================================================]]
2151function SetLogLevel(level)
2152 LOG:SetLogLevel(level)
2153end
2154
2155--[[=============================================================================
2156 LogLevel()
2157
2158 Description:
2159 Returns the currently set log level
2160
2161 Parameters:
2162 None
2163
2164 Returns:
2165 The current log level
2166 0 = Fatal
2167 1 = Error
2168 2 = Warn
2169 3 = Info
2170 4 = Debug
2171 5 = Trace
2172===============================================================================]]
2173function LogLevel()
2174 return LOG:LogLevel()
2175end
2176
2177--[[=============================================================================
2178 OutputPrint(value)
2179
2180 Description:
2181 Specifies whether to output log messages or not
2182
2183 Parameters:
2184 value(bool) - true to enable logging output, false otherwise
2185
2186 Returns:
2187 None
2188===============================================================================]]
2189function OutputPrint(value)
2190 LOG:OutputPrint(value)
2191end
2192
2193--[[=============================================================================
2194 OutputPrint(value)
2195
2196 Description:
2197 Specifies whether to output log messages to file or not
2198
2199 Parameters:
2200 value(bool) - true to enable logging output, false otherwise
2201
2202 Returns:
2203 None
2204===============================================================================]]
2205function OutputC4Log(value)
2206 LOG:OutputC4Log(value)
2207end
2208
2209--[[=============================================================================
2210 SetLogName(logName)
2211
2212 Description:
2213 Sets the name of the log file where the messages will be written to
2214
2215 Parameters:
2216 logName(string) - Sets the name of the log to write messages to
2217
2218 Returns:
2219 None
2220===============================================================================]]
2221function SetLogName(logName)
2222 LOG:SetLogName(logName)
2223end
2224
2225--[[=============================================================================
2226 LogName(logName)
2227
2228 Description:
2229 Gets the name of the log file where the messages will be written to
2230
2231 Parameters:
2232 None
2233
2234 Returns:
2235 The value of the log file that has been set
2236===============================================================================]]
2237function LogName()
2238 return LOG:LogName()
2239end
2240
2241--[[=============================================================================
2242 LogEnabled()
2243
2244 Description:
2245 Identifies if logging or print has been enabled
2246
2247 Parameters:
2248 None
2249
2250 Returns:
2251 true if either logging or print has been enabled, false otherwise
2252===============================================================================]]
2253function LogEnabled()
2254 return LOG:Enabled()
2255end
2256
2257--[[=============================================================================
2258 PrintEnabled()
2259
2260 Description:
2261 Gets the state of print output
2262
2263 Parameters:
2264 None
2265
2266 Returns:
2267 true if print has been enabled, false otherwise
2268===============================================================================]]
2269function PrintEnabled()
2270 return LOG:PrintEnabled()
2271end
2272
2273--[[=============================================================================
2274 C4LogEnabled()
2275
2276 Description:
2277 Gets the state of logging
2278
2279 Parameters:
2280 None
2281
2282 Returns:
2283 true if logging has been enabled, false otherwise
2284===============================================================================]]
2285function C4LogEnabled()
2286 return LOG:C4LogEnabled()
2287end
2288
2289--[[=============================================================================
2290 LogFatal(sLogText, ...)
2291
2292 Description:
2293 Formats and prints a series of characters and values to the enabled outputs
2294 when the set logging level is Fatal(0) or higher
2295
2296 Parameters:
2297 sLogText(string) - Format control string
2298 ... - Optional arguments which will replace all the format
2299 specifiers contained in the format string
2300
2301 Returns:
2302 None
2303===============================================================================]]
2304function LogFatal(sLogText, ...)
2305 local status, err = pcall(TryLog, 0, sLogText, ...)
2306 if (not status) then
2307 LOG:Print(1, "LUA_ERROR - LogFatal failed: " .. err)
2308 end
2309end
2310
2311--[[=============================================================================
2312 LogError(sLogText, ...)
2313
2314 Description:
2315 Formats and prints a series of characters and values to the enabled outputs
2316 when the set logging level is Error(1) or higher
2317
2318 Parameters:
2319 sLogText(string) - Format control string
2320 ... - Optional arguments which will replace all the format
2321 specifiers contained in the format string
2322
2323 Returns:
2324 None
2325===============================================================================]]
2326function LogError(sLogText, ...)
2327 local status, err = pcall(TryLog, 1, sLogText, ...)
2328 if (not status) then
2329 LOG:Print(1, "LUA_ERROR - LogError failed: " .. err)
2330 end
2331end
2332
2333--[[=============================================================================
2334 LogWarn(sLogText, ...)
2335
2336 Description:
2337 Formats and prints a series of characters and values to the enabled outputs
2338 when the set logging level is Warn(2) or higher
2339
2340 Parameters:
2341 sLogText(string) - Format control string
2342 ... - Optional arguments which will replace all the format
2343 specifiers contained in the format string
2344
2345 Returns:
2346 None
2347===============================================================================]]
2348function LogWarn(sLogText, ...)
2349 local status, err = pcall(TryLog, 2, sLogText, ...)
2350 if (not status) then
2351 LOG:Print(1, "LUA_ERROR - LogWarn failed: " .. err)
2352 end
2353end
2354
2355--[[=============================================================================
2356 LogInfo(sLogText, ...)
2357
2358 Description:
2359 Formats and prints a series of characters and values to the enabled outputs
2360 when the set logging level is Info(3) or higher
2361
2362 Parameters:
2363 sLogText(string) - Format control string
2364 ... - Optional arguments which will replace all the format
2365 specifiers contained in the format string
2366
2367 Returns:
2368 None
2369===============================================================================]]
2370function LogInfo(sLogText, ...)
2371 local status, err = pcall(TryLog, 3, sLogText, ...)
2372 if (not status) then
2373 LOG:Print(1, "LUA_ERROR - LogInfo failed: " .. err)
2374 end
2375end
2376
2377--[[=============================================================================
2378 LogDebug(sLogText, ...)
2379
2380 Description:
2381 Formats and prints a series of characters and values to the enabled outputs
2382 when the set logging level is Debug(4) or higher
2383
2384 Parameters:
2385 sLogText(string) - Format control string
2386 ... - Optional arguments which will replace all the format
2387 specifiers contained in the format string
2388
2389 Returns:
2390 None
2391===============================================================================]]
2392function LogDebug(sLogText, ...)
2393 local status, err = pcall(TryLog, 4, sLogText, ...)
2394 if (not status) then
2395 LOG:Print(1, "LUA_ERROR - LogDebug failed: " .. err)
2396 end
2397end
2398
2399--[[=============================================================================
2400 LogTrace(sLogText, ...)
2401
2402 Description:
2403 Formats and prints a series of characters and values to the enabled outputs
2404 when the set logging level is Trace(5) or higher
2405
2406 Parameters:
2407 sLogText(string) - Format control string
2408 ... - Optional arguments which will replace all the format
2409 specifiers contained in the format string
2410
2411 Returns:
2412 None
2413===============================================================================]]
2414function LogTrace(sLogText, ...)
2415 local status, err = pcall(TryLog, 5, sLogText, ...)
2416 if (not status) then
2417 LOG:Print(1, "LUA_ERROR - LogTrace failed: " .. err)
2418 end
2419end
2420
2421function dbgPrint(buf)
2422 if (LOG:PrintEnabled()) then
2423 print (buf)
2424 end
2425end
2426
2427function dbgHexdump(buf)
2428 hexdump(buf, dbgPrint)
2429end
2430
2431--[[=============================================================================
2432 c4_log unit tests
2433===============================================================================]]
2434function __test_c4_log()
2435 require "test.C4Virtual"
2436
2437 local LOG = c4_log:new("test_c4_log")
2438 assert(LOG:LogName() == "test_c4_log", "_logName is not equal to 'test_c4_log' it is: " .. LOG:LogName())
2439
2440 -- Test setting log level
2441 LOG:SetLogLevel("2 - Warning")
2442 assert(LOG:LogLevel() == 2, "_logLevel is not equal to '2' it is: " .. LOG:LogLevel())
2443
2444 LOG:SetLogLevel(3)
2445 assert(LOG:LogLevel() == 3, "_logLevel is not equal to '3' it is: " .. LOG:LogLevel())
2446
2447 -- Test enabling logs
2448 LOG:OutputPrint(false)
2449 assert(LOG:PrintEnabled() == false, "_outputPrint is not equal to 'false' it is: " .. tostring(LOG:PrintEnabled()))
2450
2451 LOG:OutputC4Log(true)
2452 assert(LOG:C4LogEnabled() == true, "_outputC4Log is not equal to 'true' it is: " .. tostring(LOG:C4LogEnabled()))
2453
2454 LOG:SetLogLevel(4)
2455 LogTrace("***** This is a test *****")
2456end
2457
2458function __test_CreatTableText()
2459 local tTest = {}
2460
2461 tTest[1] = {}
2462 tTest[2] = {}
2463 tTest[3] = 30
2464 tTest[4] = "Forty"
2465
2466 LogTrace("----- tText -----")
2467 LogTrace(tTest)
2468
2469 local tTest2 = { One = {},
2470 Two = {},
2471 Three = 30,
2472 Four = "Forty" }
2473 LogTrace("----- tText2 -----")
2474 LogTrace(tTest2)
2475
2476 local tTest3 = { [1] = {},
2477 [2] = {},
2478 [3] = 30,
2479 [4] = "Forty" }
2480 LogTrace("----- tText3 -----")
2481 LogTrace(tTest3)
2482
2483 local tTest4 = { [1] = {},
2484 Two = {},
2485 [3] = 30,
2486 [4] = "Forty",
2487 Five = "Fifty" }
2488 LogTrace("----- tText4 -----")
2489 LogTrace(tTest4)
2490
2491 local tableText = LOG:CreateTableText(tTest4)
2492 LogTrace("----- tableText -----")
2493 LogTrace(tableText)
2494
2495 --local tNew = {[1] = {},[3] = 30,[4] = 'Forty',Five = 'Fifty',Two = {},}
2496 --LogTrace(tNew)
2497end
2498
2499function __TestCreateTableTextFormatted()
2500 require "test.C4Virtual"
2501
2502 local LOG = c4_log:new("test_c4_log")
2503 local tButtons = {
2504 Name = 'heat',
2505 Attributes = {},
2506 ChildNodes = {
2507 [1] = {
2508 Name = 'button',
2509 Attributes = {},
2510 ChildNodes = {
2511 [1] = {
2512 Value = '51',
2513 Attributes = {},
2514 Name = 'id',
2515 ChildNodes = {},
2516 },
2517 [2] = {
2518 Value = 'Pool Heater',
2519 Attributes = {},
2520 Name = 'button_text',
2521 ChildNodes = {},
2522 },
2523 [3] = {
2524 Value = 'POOLHT',
2525 Attributes = {},
2526 Name = 'button_name',
2527 ChildNodes = {},
2528 },
2529 },
2530 },
2531 [2] = {
2532 Name = 'button',
2533 Attributes = {},
2534 ChildNodes = {
2535 [1] = {
2536 Value = '53',
2537 Attributes = {},
2538 Name = 'id',
2539 ChildNodes = {},
2540 },
2541 [2] = {
2542 Value = 'Spa Heater',
2543 Attributes = {},
2544 Name = 'button_text',
2545 ChildNodes = {},
2546 },
2547 [3] = {
2548 Value = 'SPAHT',
2549 Attributes = {},
2550 Name = 'button_name',
2551 ChildNodes = {},
2552 },
2553 },
2554 },
2555 [3] = {
2556 Name = 'button',
2557 Attributes = {},
2558 ChildNodes = {
2559 [1] = {
2560 Value = '54',
2561 Attributes = {},
2562 Name = 'id',
2563 ChildNodes = {},
2564 },
2565 [2] = {Value = 'Pool Solar Heater',
2566 Attributes = {},
2567 Name = 'button_text',
2568 ChildNodes = {}
2569 },
2570 [3] = {
2571 Value = 'SOLHT',
2572 Attributes = {},
2573 Name = 'button_name',
2574 ChildNodes = {},
2575 },
2576 }
2577 }
2578 }
2579 }
2580
2581 print(LOG:CreateTableTextFormatted(tButtons))
2582end end)
2583package.preload['lib.c4_object'] = (function (...)
2584--[[=============================================================================
2585 c4_object Class
2586
2587 Copyright 2016 Control4 Corporation. All Rights Reserved.
2588===============================================================================]]
2589
2590-- Set template version for this file
2591if (TEMPLATE_VERSION ~= nil) then
2592 TEMPLATE_VERSION.c4_object = "2016.01.08"
2593end
2594
2595function inheritsFrom( baseClass )
2596 local new_class = {}
2597 local class_mt = { __index = new_class }
2598
2599 function new_class:create(...)
2600 local newinst = {}
2601
2602 setmetatable( newinst, class_mt )
2603
2604 -- Call the constructor when we create this class
2605 if newinst.construct then
2606 -- Allow returning a different obj than self. This allows for readonly tables etc...
2607 newinst = newinst:construct(...) or newinst
2608 end
2609
2610 return newinst
2611 end
2612
2613 if nil ~= baseClass then
2614 setmetatable( new_class, { __index = baseClass } )
2615 end
2616
2617 --[[=============================================================================
2618 Implementation of additional OO properties starts here
2619 ===============================================================================]]
2620
2621 -- Return the class object of the instance
2622 function new_class:class()
2623 return new_class
2624 end
2625
2626 --[[=============================================================================
2627 Return the super class object of the instance.
2628
2629 Note Calling methods on the base class itself will modify
2630 the base table's static properties. In order to have call
2631 the base class' methods and have them modify the current object
2632 use super() or superAsSelf().
2633 ===============================================================================]]
2634 function new_class:superClass()
2635 return baseClass
2636 end
2637
2638 --[[=============================================================================
2639 Returns a table that allows calling of the base class's method
2640 while maintaining the objects state as the modified state of the base
2641 class' methods. For example consider the following statements (order matters):
2642
2643 -- The child sees the parents property if the child hasn't overriden the property
2644 obj:superClass().id = "parent"
2645 obj.id == "parent" -- true
2646
2647 -- Setting the property on the child overrides (hides) the parents property
2648 obj.id = "child"
2649 obj.id == "child" -- true
2650
2651 -- The super() method pass
2652 obj:super().id == "parent" -- true
2653 obj:super().id = "child"
2654 obj:super().id == "parent" -- still true
2655 obj.id == "child" -- still true
2656 ===============================================================================]]
2657 function new_class:super()
2658 local holder = {}
2659
2660 holder.child = self
2661 holder.parent = baseClass
2662
2663 local mt = {}
2664 mt.__index = function(table, index)
2665 if table.parent[index] then
2666 return table.parent[index]
2667 else
2668 return table.child[index]
2669 end
2670 end
2671
2672 -- Only set the new values to the child.
2673 mt.__newindex = function(table, key, value)
2674 table.child[key] = value
2675 end
2676
2677 mt.__tostring = function(table)
2678 return tostring(table.child)
2679 end
2680
2681 setmetatable(holder, mt)
2682 return holder
2683 end
2684
2685 new_class.new = new_class.create
2686
2687 --[[=============================================================================
2688 Return true if the caller is an instance of theClass
2689 ===============================================================================]]
2690 function new_class:isa( theClass )
2691 local b_isa = false
2692 local cur_class = new_class
2693
2694 while ( nil ~= cur_class ) and ( false == b_isa ) do
2695 if cur_class == theClass then
2696 b_isa = true
2697 else
2698 cur_class = cur_class:superClass()
2699 end
2700 end
2701
2702 return b_isa
2703 end
2704
2705 return new_class
2706end
2707
2708--[[=============================================================================
2709 Inheritance unit tests
2710===============================================================================]]
2711function __test_inheritance()
2712 local b = inheritsFrom(nil)
2713
2714 b.construct = function(self, msg)
2715 self._msg = msg
2716 end
2717
2718 local t = inheritsFrom(b)
2719 t.construct = function(self, msg)
2720 self:super():construct(msg)
2721 end
2722
2723 t1 = t:new("t1")
2724 t2 = t:new("t2")
2725 assert(t1._msg == "t1", "t1 message is not equal to 't1' it''s: " .. t1._msg)
2726 assert(t2._msg == "t2", "t2 message is not equal to 't2' it''s: " .. t2._msg)
2727 assert(tostring(t1:super()) == tostring(t1), "tostrings don't match");
2728 assert(t1:superClass() == b, "superClass and baseClass should be the same. They are not.")
2729
2730 t1:superClass().id = "parent"
2731 assert(t1.id == "parent", "obect''s super class has invalid property value: ", t1.id)
2732
2733 -- Setting the property on the child overrides (hides) the parents property
2734 t1.id = "child"
2735 assert(t1.id == "child", "object instance variable has invalid property value: " .. t1.id)
2736
2737 -- The super() method maintains the self pointer to the child and not to the base
2738 assert(t1:super().id == "parent", "superAsSelf found invalid value for base class variable")
2739 t1:super().id = "child1"
2740 assert(t1:super().id == "parent", "Setting of instance variable hid base classes variable from itself");
2741 assert(t1.id == "child1", "Settings of instance variable did not change child instance variable")
2742end end)
2743package.preload['lib.c4_queue'] = (function (...)
2744--[[=============================================================================
2745 c4_queue Class
2746
2747 Copyright 2016 Control4 Corporation. All Rights Reserved.
2748===============================================================================]]
2749require "common.c4_driver_declarations"
2750require "lib.c4_object"
2751
2752-- Set template version for this file
2753if (TEMPLATE_VERSION ~= nil) then
2754 TEMPLATE_VERSION.c4_queue = "2016.01.08"
2755end
2756
2757c4_queue = inheritsFrom(nil)
2758
2759function c4_queue:construct()
2760 -- entry table
2761 self._et = {first = 0, last = -1}
2762 self._maxSize = 0 -- no size limit
2763 self._name = ""
2764
2765 local mt = getmetatable(self)
2766 if (mt ~= nil) then
2767 mt.__tostring = self.__tostring
2768 end
2769end
2770
2771function c4_queue:__tostring()
2772 local tOutputString = {}
2773 table.insert(tOutputString, "--- Queue ---")
2774 if (not IsEmpty(self._name)) then
2775 table.insert(tOutputString, " name = " .. tostring(self._name))
2776 end
2777 table.insert(tOutputString, " first = " .. tostring(self._et.first))
2778 table.insert(tOutputString, " last = " .. tostring(self._et.last))
2779 table.insert(tOutputString, " number in queue = " .. tostring(self._et.last - self._et.first + 1))
2780 table.insert(tOutputString, " maximum size = " .. self._maxSize)
2781 table.insert(tOutputString, " next value = " .. tostring(self:value()))
2782 return table.concat(tOutputString, "\n")
2783end
2784
2785
2786-- push a value on the queue
2787function c4_queue:push(value, ...)
2788 local numItems = self._et.last - self._et.first + 1
2789
2790 if ((self._maxSize <= 0) or (numItems < self._maxSize) ) then
2791 local last = self._et.last + 1
2792 self._et.last = last
2793
2794 local interval = select(1, ...)
2795 local units = select(2, ...)
2796 local command_name = select(3, ...)
2797 self._et[last] = {["command"] = value, ["command_delay"] = interval, ["delay_units"] = units, ["command_name"] = command_name}
2798 --LogTrace ("Queue:push(), first = " .. tostring(self._et.first) .. ", last = " .. tostring(self._et.last) .. ", number in queue = " .. tostring(self._et.last - self._et.first + 1) .. ", value = " .. value)
2799 else
2800 -- if addToQueue == true then push value to queue
2801 if (self:OnMaxSizeReached()) then
2802 local last = self._et.last + 1
2803 self._et.last = last
2804 self._et[last] = {["command"] = value, ["command_delay"] = interval, ["delay_units"] = units, ["command_name"] = command_name}
2805 --LogTrace ("Queue:push(), first = " .. tostring(self._et.first) .. ", last = " .. tostring(self._et.last) .. ", number in queue = " .. tostring(self._et.last - self._et.first + 1) .. ", value = " .. value)
2806 end
2807 end
2808end
2809
2810function c4_queue:OnMaxSizeReached()
2811 --LogTrace ("Max Size Reached - clear queue and push value to the queue (default).")
2812 local addToQueue = true
2813
2814 self:clear()
2815 return (addToQueue)
2816end
2817
2818-- pop a value from the queue
2819function c4_queue:pop()
2820 local first = self._et.first
2821
2822 if first > self._et.last then
2823 --LogTrace("Queue:pop(), queue is empty")
2824 return ""
2825 end
2826
2827 local value = self._et[first]
2828 self._et[first] = nil -- to allow garbage collection
2829 self._et.first = first + 1
2830 --LogTrace ("Queue:pop(), first = " .. tostring(self._et.first) .. ", last = " .. tostring(self._et.last) .. ", number in queue = " .. tostring(self._et.last - self._et.first + 1) .. ", value = " .. value)
2831
2832 return value
2833end
2834
2835-- clear queue
2836function c4_queue:clear()
2837 local first = self._et.first
2838
2839 if first > self._et.last then
2840 --LogTrace ("Queue:clear(), queue is empty")
2841 return ""
2842 end
2843
2844 self._et = {first = 0, last = -1}
2845 --LogTrace ("Queue:clear(), first = " .. tostring(self._et.first) .. ", last = " .. tostring(self._et.last) .. ", number in queue = " .. tostring(self._et.last - self._et.first + 1))
2846 --LogTrace (self._et)
2847end
2848
2849-- return value of first item
2850function c4_queue:value()
2851 local first = self._et.first
2852
2853 if (first > self._et.last) then
2854 return ""
2855 else
2856 return self._et[first]
2857 end
2858end
2859
2860-- return queue's maximum size
2861function c4_queue:MaxSize()
2862 return self._maxSize
2863end
2864
2865-- return queue's maximum size
2866function c4_queue:SetMaxSize(size)
2867 self._maxSize = size
2868end
2869
2870function c4_queue:SetName(name)
2871 self._name = name
2872end
2873
2874-- return the queue's current size
2875function c4_queue:Size()
2876 return self._et.last - self._et.first + 1
2877end
2878
2879-- is queue empty?
2880function c4_queue:empty()
2881 -- print ("self._et.first = " .. tostring(self._et.first) .. ", self._et.last = " .. tostring(self._et.last))
2882 if (self._et.first > self._et.last) then
2883 return true
2884 else
2885 return false
2886 end
2887end
2888
2889--[[
2890 c4_queue unit tests
2891--]]
2892function __test_c4_queue()
2893 require "test.C4Virtual"
2894 require "lib.c4_log"
2895
2896 local LOG = c4_log:new("test_c4_queue")
2897 LOG:SetLogLevel(5)
2898 LOG:OutputPrint(true)
2899
2900 -- create an instance of the queue
2901 local c4Queue = c4_queue:new()
2902
2903 c4Queue:SetMaxSize(3)
2904 assert(c4Queue:MaxSize() == 3, "_maxSize is not equal to '3' it is: " .. c4Queue:MaxSize())
2905
2906 c4Queue:push("Item #1 in Queue")
2907 c4Queue:push("Item #2 in Queue")
2908 c4Queue:push("Item #3 in Queue")
2909 c4Queue:push("Item #4 in Queue") -- this should cause OnMaxSizeReached() to be called and clear the queue
2910 assert(c4Queue:Size() == 1, "queue size is not equal to '1' it is: " .. c4Queue:Size())
2911
2912 print (c4Queue)
2913
2914 -- Test inheritance overriding OnMaxSizeReached
2915 -- Create a new class
2916 c4_queue_new = inheritsFrom(c4_queue)
2917
2918 -- override construct()
2919 function c4_queue_new:construct()
2920 self.superClass():construct() -- call base class
2921 self._maxSizeOption = 1
2922
2923 local mt = getmetatable(self)
2924 if (mt ~= nil) then
2925 mt.__tostring = self.__tostring
2926 end
2927 end
2928
2929 -- override OnMaxSizeReached()
2930 function c4_queue_new:OnMaxSizeReached()
2931 --Default: clear queue and push value to the queue. (No need to overload,
2932
2933 -- Option 1: Do Nothing, new item is not added to queue
2934 if (self._maxSizeOption == 1) then
2935
2936 LogInfo("Max Size Reached - do nothing, new item not added to queue (option 1)")
2937 return (false)
2938 -- Option 2: pop value, and push new value on queue
2939 elseif(self._maxSizeOption == 2) then
2940 LogInfo("Max Size Reached - pop value, and push new value on queue (option 2)")
2941 self:pop()
2942 return (true)
2943 -- Option 3: clear queue and DO NOT push new value onto queue
2944 elseif(self._maxSizeOption == 3) then
2945 LogInfo("Max Size Reached - clear queue and DO NOT push new value onto queue")
2946 self:clear()
2947 return (false)
2948 end
2949 end
2950
2951 -- create an instance of the new queue
2952 local c4QueueNew = c4_queue_new:new()
2953 c4QueueNew:SetMaxSize(3)
2954 c4QueueNew:push("Item #1 in Queue")
2955 c4QueueNew:push("Item #2 in Queue")
2956 c4QueueNew:push("Item #3 in Queue")
2957 c4QueueNew:push("Item #4 in Queue") -- this should cause OnMaxSizeReached() to be called and clear the queue
2958 assert(c4QueueNew:Size() == 3, "queue size is not equal to '3' it is: " .. c4QueueNew:Size())
2959
2960 print(c4QueueNew)
2961 print ("done...")
2962end end)
2963package.preload['lib.c4_timer'] = (function (...)
2964--[[=============================================================================
2965 c4_timer Class
2966
2967 Copyright 2017 Control4 Corporation. All Rights Reserved.
2968===============================================================================]]
2969require "common.c4_driver_declarations"
2970require "lib.c4_object"
2971
2972-- Set template version for this file
2973if (TEMPLATE_VERSION ~= nil) then
2974 TEMPLATE_VERSION.c4_timer = "2017.05.03"
2975end
2976
2977c4_timer = inheritsFrom(nil)
2978
2979function c4_timer:construct(name, interval, units, Callback, repeating, CallbackParam)
2980 self._name = name
2981 self._timerID = TimerLibGetNextTimerID()
2982 self._interval = interval
2983 self._units = units
2984 self._repeating = repeating or false
2985 self._Callback = Callback
2986 self._CallbackParam = CallbackParam or ""
2987 self._id = 0
2988
2989 gTimerLibTimers[self._timerID] = self
2990 if (LOG ~= nil and type(LOG) == "table") then
2991 LogTrace("Created timer " .. self._name)
2992 end
2993end
2994
2995function c4_timer:StartTimer(...)
2996 c4_timer:KillTimer()
2997
2998 -- optional parameters (interval, units, repeating)
2999 if ... then
3000 local interval = select(1, ...)
3001 local units = select(2, ...)
3002 local repeating = select(3, ...)
3003
3004 self._interval = interval or self._interval
3005 self._units = units or self._units
3006 self._repeating = repeating or self._repeating
3007 end
3008
3009 if (tonumber(self._interval) > 0) then
3010 if (LOG ~= nil and type(LOG) == "table") then
3011 LogTrace("Starting Timer: " .. self._name)
3012 end
3013
3014 self._id = C4:AddTimer(self._interval, self._units, self._repeating)
3015 end
3016end
3017
3018function c4_timer:KillTimer()
3019 if (self._id) then
3020 self._id = C4:KillTimer(self._id)
3021 end
3022end
3023
3024function c4_timer:TimerStarted()
3025 return (self._id ~= 0)
3026end
3027
3028function c4_timer:TimerStopped()
3029 return (self._id == 0)
3030end
3031
3032function c4_timer:GetTimerInterval()
3033 return (self._interval)
3034end
3035
3036function TimerLibGetNextTimerID()
3037 gTimerLibTimerCurID = gTimerLibTimerCurID + 1
3038 return gTimerLibTimerCurID
3039end
3040
3041function ON_DRIVER_EARLY_INIT.c4_timer()
3042 gTimerLibTimers = {}
3043 gTimerLibTimerCurID = 0
3044end
3045
3046function ON_DRIVER_DESTROYED.c4_timer()
3047 -- Kill open timers
3048 for k,v in pairs(gTimerLibTimers) do
3049 v:KillTimer()
3050 end
3051end
3052
3053--[[=============================================================================
3054 OnTimerExpired(idTimer)
3055
3056 Description:
3057 Function called by Director when the specified Control4 timer expires.
3058
3059 Parameters:
3060 idTimer(string) - Timer ID of expired timer.
3061===============================================================================]]
3062function OnTimerExpired(idTimer)
3063 for k,v in pairs(gTimerLibTimers) do
3064 if (idTimer == v._id) then
3065 if (v._Callback) then
3066 v._Callback(v._CallbackParam)
3067 end
3068 end
3069 end
3070end
3071
3072--[[=============================================================================
3073 CreateTimer(name, interval, units, callback, repeating, callbackParam)
3074
3075 Description:
3076 Creates a named timer with the given attributes
3077
3078 Parameters:
3079 name(string) - The name of the timer being created
3080 interval(int) - The amount of the given time between calls to the
3081 timers callback function
3082 units(string) - The time of time interval used (e.g. MILLSECONDS, SECONDS, MINUTES, HOURS)
3083 callback(string) - The function to call when the timer expires
3084 repeating(bool) - Parameter indicating whether the timer should be
3085 called repeatedly until cancelled
3086 callbackParam(...) - Parameters to be passed to the callback function
3087
3088 Returns:
3089 A handle to the timer
3090===============================================================================]]
3091function CreateTimer(name, interval, units, callback, repeating, callbackParam)
3092 timer = c4_timer:new(name, interval, units, callback, repeating, callbackParam)
3093 return timer
3094end
3095
3096--[[=============================================================================
3097 StartTimer(handle, ...)
3098
3099 Description:
3100 Starts the timer created by calling the CreateTimer functions
3101
3102 Parameters:
3103 handle(timer) - Handle to a created timer object
3104 interval(int) - The amount of the given time between calls to the
3105 timers callback function
3106 units(string) - The time of time interval used (e.g. SECONDS, MINUTES, ...)
3107 repeating(bool) - Parameter indicating whether the timer should be
3108 called repeatedly until cancelled
3109
3110 Returns:
3111 None
3112===============================================================================]]
3113function StartTimer(handle, ...)
3114 handle:StartTimer(...)
3115end
3116
3117--[[=============================================================================
3118 KillTimer(handle)
3119
3120 Description:
3121 Starts the timer created by calling the CreateTimer functions
3122
3123 Parameters:
3124 handle(timer) - Handle to a created timer object
3125
3126 Returns:
3127 None
3128===============================================================================]]
3129function KillTimer(handle)
3130 handle:KillTimer()
3131end
3132
3133--[[=============================================================================
3134 TimerStarted(handle)
3135
3136 Description:
3137 Identifies whether a timer has been started or not
3138
3139 Parameters:
3140 handle(timer) - Handle to a created timer object
3141
3142 Returns:
3143 Returns true if a the given timer handle has been started, or false otherwise
3144===============================================================================]]
3145function TimerStarted(handle)
3146 return handle:TimerStarted()
3147end
3148
3149--[[=============================================================================
3150 TimerStopped(handle)
3151
3152 Description:
3153 Identifies whether a timer has been stopped or not
3154
3155 Parameters:
3156 handle(timer) - Handle to a created timer object
3157
3158 Returns:
3159 Returns true if a the given timer handle has been stopped, or false otherwise
3160===============================================================================]]
3161function TimerStopped(handle)
3162 return handle:TimerStopped()
3163end
3164
3165--[[=============================================================================
3166 GetTimerInterval(handle)
3167
3168 Description:
3169 Gets the interval setting of the given timer
3170
3171 Parameters:
3172 handle(timer) - Handle to a created timer object
3173
3174 Returns:
3175 Returns the interval setting of the given timer
3176===============================================================================]]
3177function GetTimerInterval(handle)
3178 return handle:GetTimerInterval()
3179end
3180
3181--[[=============================================================================
3182 c4_timer Unit Tests
3183===============================================================================]]
3184function __test_c4_timer()
3185 require "test.C4Virtual"
3186 require "lib.c4_log"
3187 require "common.c4_init"
3188
3189 OnDriverInit()
3190
3191 local LOG = c4_log:new("test_c4_timer")
3192 LOG:SetLogLevel(5)
3193 LOG:OutputPrint(true)
3194
3195 function OnTestTimerExpired()
3196 c4Timer:KillTimer()
3197 end
3198
3199 -- create an instance of the timer
3200 c4Timer = c4_timer:new("Test", 45, "MINUTES", OnTestTimerExpired)
3201
3202 assert(c4Timer._id == 0, "_id is not equal to '0' it is: " .. c4Timer._id)
3203 c4Timer:StartTimer()
3204 assert(c4Timer._id == 10001, "_id is not equal to '10001' it is: " .. c4Timer._id)
3205 assert(c4Timer:TimerStarted() == true, "TimerStarted is not equal to true it is: " .. tostring(c4Timer:TimerStarted()))
3206 assert(c4Timer:TimerStopped() == false, "TimerStopped is not equal to false it is: " .. tostring(c4Timer:TimerStopped()))
3207 OnTimerExpired(c4Timer._id)
3208 assert(c4Timer:TimerStarted() == false, "TimerStarted is not equal to false it is: " .. tostring(c4Timer:TimerStarted()))
3209 assert(c4Timer:TimerStopped() == true, "TimerStopped is not equal to true it is: " .. tostring(c4Timer:TimerStopped()))
3210end end)
3211package.preload['lib.c4_xml'] = (function (...)
3212--[[=============================================================================
3213 Functions for parsing and managing xml
3214
3215 Copyright 2016 Control4 Corporation. All Rights Reserved.
3216===============================================================================]]
3217
3218if (TEMPLATE_VERSION ~= nil) then
3219 TEMPLATE_VERSION.c4_xml = "2016.01.08"
3220end
3221
3222--[[=============================================================================
3223 GetParsedXmlNode(tXml, node)
3224
3225 Description:
3226 Find the specified node within the given table
3227
3228 Parameters:
3229 tXml(table) - Xml fragment containing the node we are looking for
3230 node(string) - The name of the node
3231
3232 Returns:
3233 nil or the specified node within the table
3234===============================================================================]]
3235function GetParsedXmlNode(tXml, node)
3236 for k, v in pairs(tXml["ChildNodes"]) do
3237 if (v["Name"] == node) then
3238 return v["ChildNodes"]
3239 end
3240 end
3241
3242 return nil
3243end
3244
3245--[[=============================================================================
3246 GetParsedXmlValuesByKey(tXml, node, key, keyIsNumber)
3247
3248 Description:
3249 Find the specified node element within the given table
3250
3251 Parameters:
3252 tXml(table) - Xml fragment to find the value in
3253 node(string) - The name of the node
3254 key(string) - The name of the key
3255 keyIsNumber(bool) - Indicates whether the table index is a number or a string
3256
3257 Returns:
3258 nil or a table of the found values within the Xml
3259===============================================================================]]
3260function GetParsedXmlValuesByKey(tXml, node, key, keyIsNumber)
3261 local tParams = {}
3262
3263 keyIsNumber = keyIsNumber or false
3264 for k,v in pairs(tXml) do
3265 if (v["Name"] == node) then
3266 local keyValue
3267
3268 -- get the key
3269 for nodeKey, nodeValue in pairs(v["ChildNodes"]) do
3270 if (nodeValue["Name"] == key) then
3271 if (keyIsNumber == true) then
3272 keyValue = tonumber(nodeValue.Value)
3273 else
3274 keyValue = tostring(nodeValue.Value)
3275 end
3276 break
3277 end
3278 end
3279
3280 -- get other tags
3281 tParams[keyValue] = {}
3282 for nodeKey, nodeValue in pairs(v["ChildNodes"]) do
3283 if (nodeValue["Name"] ~= key) then
3284 tParams[keyValue][nodeValue.Name] = nodeValue.Value
3285 end
3286 end
3287 end
3288 end
3289
3290 return tParams
3291end
3292
3293--[[=============================================================================
3294 GetParsedXmlVaulesByKeyAttribute(tXml, node, key, keyIsNumber)
3295
3296 Description:
3297 Find the specified node attribute within the given table
3298
3299 Parameters:
3300 tXml(table) - Xml fragment to find the value in
3301 node(string) - The name of the node
3302 key(string) - The name of the key
3303 keyIsNumber(bool) - Indicates whether the table index is a number or a string
3304
3305 Returns:
3306 nil or a table of the found values within the Xml
3307===============================================================================]]
3308function GetParsedXmlVaulesByKeyAttribute(tXml, node, key, keyIsNumber)
3309 local tParams = {}
3310
3311 keyIsNumber = keyIsNumber or false
3312 for k,v in pairs(tXml["ChildNodes"]) do
3313 if (v["Name"] == node) then
3314 local keyValue
3315
3316 if (keyIsNumber == true) then
3317 keyValue = tonumber(v["Attributes"][key])
3318 else
3319 keyValue = v["Attributes"][key]
3320 end
3321
3322 tParams[keyValue] = v["Value"]
3323 end
3324 end
3325
3326 return tParams
3327end
3328
3329--[[=============================================================================
3330 BuildSimpleXml(tag, tData, escapeValue)
3331
3332 Description:
3333 Find the specified node within the given table
3334
3335 Parameters:
3336 tag(string) - Xml tag name to create
3337 tData(table) - key value pairs that will be added as elements under tag
3338 escapeValue(bool) - Indicates whether the values should be escaped or not
3339
3340 Returns:
3341 nil or an Xml fragment the specified node within the table
3342===============================================================================]]
3343function BuildSimpleXml(tag, tData, escapeValue)
3344 local xml = ""
3345
3346 if (tag ~= nil) then
3347 xml = "<" .. tag .. ">"
3348 end
3349
3350 for k,v in pairs(tData) do
3351 xml = xml .. "<" .. k
3352 if (type(v) == "table") then
3353 -- handle attributes
3354 for kAttrib, vAttrib in pairs(v.attributes) do
3355 xml = xml .. ' ' .. kAttrib .. '=\"' .. vAttrib .. '\"'
3356 end
3357 xml = xml .. ">" .. InsertValue(v.value, escapeValue) .. "</" .. k .. ">"
3358 else
3359 xml = xml .. ">" .. InsertValue(v, escapeValue) .. "</" .. k .. ">"
3360 end
3361 end
3362
3363 if (tag ~= nil) then
3364 xml = xml .. "</" .. tag .. ">"
3365 end
3366
3367 --DbgTrace("BuildSimpleXml(): " .. xml)
3368
3369 return xml
3370end
3371
3372--[[=============================================================================
3373 InsertValue(value, escapeValue)
3374
3375 Description:
3376 Return the given value if escapeValue is true it will escape any special
3377 characters in the value
3378
3379 Parameters:
3380 value(string) - value to be manipulated
3381 escapeValue(bool) - Indicates whether the values should be escaped or not
3382
3383 Returns:
3384 The value given or an escaped value if specified
3385===============================================================================]]
3386function InsertValue(value, escapeValue)
3387
3388 if (escapeValue) then
3389 value = C4:XmlEscapeString(tostring(value))
3390 end
3391
3392 return value
3393end
3394
3395--[[=============================================================================
3396 StartElement(tag)
3397
3398 Description:
3399 Wrap the given tag as an Xml element (i.e. <tag>)
3400
3401 Parameters:
3402 tag(string) - The name of the item to be wrapped as a starting Xml element
3403
3404 Returns:
3405 The value wrapped as Xml tag
3406===============================================================================]]
3407function StartElement(tag)
3408 return "<" .. tag .. ">"
3409end
3410
3411--[[=============================================================================
3412 EndElement(tag)
3413
3414 Description:
3415 Wrap the given tag as an Xml end element (i.e. </tag>)
3416
3417 Parameters:
3418 tag(string) - The name of the item to be wrapped as a ending Xml element
3419
3420 Returns:
3421 The value wrapped as ending Xml tag
3422===============================================================================]]
3423function EndElement(tag)
3424 return "</" .. tag .. ">"
3425end
3426
3427--[[=============================================================================
3428 AddElement(tag, data)
3429
3430 Description:
3431 Wrap the given tag and value as an Xml element (i.e. <tag>data</tag>)
3432
3433 Parameters:
3434 tag(string) - The name of the item to be wrapped as an Xml element
3435 data(string) - The value of the Xml element being created
3436
3437 Returns:
3438 The value wrapped as Xml tag and value
3439===============================================================================]]
3440function AddElement(tag, data)
3441 LogTrace("tag = " .. tag)
3442 LogTrace("data = " .. data)
3443
3444 return "<" .. tag .. ">" .. data .. "</" .. tag .. ">"
3445end
3446 end)
3447package.preload['panel_proxy.pgm_info'] = (function (...)
3448--[[=============================================================================
3449 PgmInformation Class
3450
3451 Copyright 2015 Control4 Corporation. All Rights Reserved.
3452===============================================================================]]
3453require "lib.c4_object"
3454require "panel_proxy.relay_notifies"
3455
3456TEMPLATE_VERSION.securitypanel = "6"
3457
3458PgmInfoList = {}
3459PgmInformation = inheritsFrom(nil)
3460
3461--[[=============================================================================
3462 Functions that are meant to be private to the class
3463===============================================================================]]
3464function PgmInformation:construct(PgmID)
3465
3466 self._PgmID = PgmID
3467 self._IsOpen = false
3468 self._NeedToSendInitialInfo = true
3469
3470 PgmInfoList[PgmID] = self
3471 NOTIFY.PANEL_ADD_PGM(self._PgmID, TheSecurityPanel._BindingID)
3472end
3473
3474function PgmInformation:destruct()
3475 NOTIFY.PANEL_REMOVE_PGM(self._PgmID, TheSecurityPanel._BindingID)
3476 PgmInfoList[self._PgmID] = nil
3477end
3478
3479function PgmInformation:PgmXML()
3480 local PgmXMLInfo = {}
3481
3482 table.insert(PgmXMLInfo, MakeXMLNode("id", tostring(self._PgmID)))
3483 table.insert(PgmXMLInfo, MakeXMLNode("is_open", tostring(self:GetPgmState())))
3484
3485 return MakeXMLNode("pgm", table.concat(PgmXMLInfo, "\n"))
3486end
3487
3488--[[=============================================================================
3489 Functions that are wrappered and meant to be exposed to the driver
3490===============================================================================]]
3491function PgmInformation:SetPgmState(IsOpen, Initializing)
3492 local JustInitializing = Initializing or false
3493
3494 if ((self._IsOpen ~= IsOpen) or self._NeedToSendInitialInfo or JustInitializing) then
3495 self._IsOpen = IsOpen
3496 LogDebug("!!!!!! Pgm %d %s !!!!!!", tonumber(self._PgmID), tostring(self:GetPgmState()))
3497 NOTIFY.PANEL_PGM_STATE(self._PgmID, self._IsOpen, self._NeedToSendInitialInfo, TheSecurityPanel._BindingID)
3498 self._NeedToSendInitialInfo = false
3499 end
3500end
3501
3502function PgmInformation:GetPgmState()
3503 return self._IsOpen
3504end end)
3505package.preload['panel_proxy.relay_commands'] = (function (...)
3506--[[=============================================================================
3507 Pgm Relay Proxy Command Functions
3508
3509 Copyright 2015 Control4 Corporation. All Rights Reserved.
3510===============================================================================]]
3511require "common.c4_command"
3512require "common.c4_utils"
3513require "panel_proxy.pgm_info"
3514
3515TEMPLATE_VERSION.securitypanel = "6"
3516
3517function PRX_CMD.PGM_OPEN(idBinding, tParams)
3518 if (TheSecurityPanel) then
3519 TheSecurityPanel:PrxSendPgmCommand(tonumber(tParams.PGM_ID), "Open", 0)
3520 end
3521end
3522
3523function PRX_CMD.PGM_CLOSE(idBinding, tParams)
3524 if (TheSecurityPanel) then
3525 TheSecurityPanel:PrxSendPgmCommand(tonumber(tParams.PGM_ID), "Close", 0)
3526 end
3527end
3528
3529function PRX_CMD.PGM_TOGGLE(idBinding, tParams)
3530 if (TheSecurityPanel) then
3531 TheSecurityPanel:PrxSendPgmCommand(tonumber(tParams.PGM_ID), "Toggle", 0)
3532 end
3533end
3534
3535function PRX_CMD.PGM_TRIGGER(idBinding, tParams)
3536 local TargPgm = PgmInfoList[tonumber(tParams.PGM_ID)]
3537 local TriggerTime = tParams["TIME"]
3538
3539 if (TheSecurityPanel and TargPgm) then
3540 TheSecurityPanel:PrxSendPgmCommand(tonumber(tParams.PGM_ID), "Trigger", TriggerTime)
3541 end
3542end
3543 end)
3544package.preload['panel_proxy.relay_notifies'] = (function (...)
3545--[[=============================================================================
3546 Notifications for the Relays (Pgms) on a security panel
3547
3548 Copyright 2015 Control4 Corporation. All Rights Reserved.
3549===============================================================================]]
3550require "common.c4_notify"
3551
3552TEMPLATE_VERSION.securitypanel = "6"
3553
3554function NOTIFY.INITIAL_RELAY_STATE(IsOpen, BindingID)
3555 LogTrace("Sending Initial Relay State on Binding %d : %s", tonumber(BindingID), tostring((IsOpen and "Open" or "Closed")))
3556 SendSimpleNotify(IsOpen and "STATE_OPENED" or "STATE_CLOSED", BindingID)
3557end
3558
3559function NOTIFY.RELAY_STATE(IsOpen, BindingID)
3560 LogTrace("Sending Relay State on Binding %d : %s", tonumber(BindingID), tostring((IsOpen and "Open" or "Closed")))
3561 SendSimpleNotify(IsOpen and "OPENED" or "CLOSED", BindingID)
3562end end)
3563package.preload['panel_proxy.securitypanel'] = (function (...)
3564--[[=============================================================================
3565 SecurityPanel Class
3566
3567 Copyright 2016 Control4 Corporation. All Rights Reserved.
3568===============================================================================]]
3569require "lib.c4_object"
3570require "panel_proxy.zone_info"
3571require "panel_proxy.pgm_info"
3572require "panel_proxy.securitypanel_commands"
3573require "panel_proxy.securitypanel_notifies"
3574require "panel_proxy.securitypanel_functions"
3575require "panel_proxy.relay_commands"
3576
3577TEMPLATE_VERSION.securitypanel = "2016.07.29"
3578
3579TheSecurityPanel = nil
3580SecurityPanel = inheritsFrom(nil)
3581
3582--[[=============================================================================
3583 Functions that are meant to be private to the class
3584===============================================================================]]
3585function SecurityPanel:construct(BindingID)
3586 self._BindingID = BindingID
3587end
3588
3589function SecurityPanel:Initialize()
3590 self:InitializeVariables()
3591end
3592
3593function SecurityPanel:InitializeVariables()
3594 self._NextTroubleIndex = 0
3595 self._TroubleTable = {}
3596end
3597
3598function SecurityPanel:GetNextTroubleID()
3599
3600 if (self._NextTroubleIndex == nil) then
3601 return 0
3602 end
3603
3604 self._NextTroubleIndex = self._NextTroubleIndex + 1
3605 return self._NextTroubleIndex
3606end
3607
3608--[[=============================================================================
3609 Functions for handling request from the Panel Proxy
3610===============================================================================]]
3611function SecurityPanel:PrxReadPanelInfo()
3612
3613 -- Force each zone remove the data guard so the value
3614 -- from the panel will be used.
3615 for _, CurZone in pairs(ZoneInfoList) do
3616 CurZone:SetDataGuardFlag(false)
3617 end
3618
3619 SecCom_ReadPanelInfo()
3620end
3621
3622function SecurityPanel:PrxGetPanelSetup()
3623 self:PrxGetAllPartitionsInfo()
3624end
3625
3626function SecurityPanel:PrxGetAllPartitionsInfo()
3627 local AllPartitionsInfos = {}
3628
3629 LogTrace("SecurityPanel.GetAllPartitionsInfo")
3630 for k, v in pairs(SecurityPartitionIndexList) do
3631 table.insert(AllPartitionsInfos, v:PartitionXML())
3632 end
3633
3634 NOTIFY.ALL_PARTITIONS_INFO(MakeXMLNode("partitions", table.concat(AllPartitionsInfos, "\n")), self._BindingID)
3635end
3636
3637function SecurityPanel:PrxGetAllZonesInfo()
3638 local AllZoneInfos = {}
3639
3640 LogTrace("SecurityPanel.GetAllZonesInfo")
3641 for k, v in pairs(ZoneInfoList) do
3642 table.insert(AllZoneInfos, v:ZonePanelXML())
3643 end
3644
3645 NOTIFY.ALL_ZONES_INFO(MakeXMLNode("zones", table.concat(AllZoneInfos, "\n")), self._BindingID)
3646end
3647
3648function SecurityPanel:PrxGetAllPgmsInfo()
3649 local AllPgmInfos = {}
3650
3651 LogTrace("SecurityPanel.GetAllPgmsInfo")
3652 for k, v in pairs(PgmInfoList) do
3653 table.insert(AllPgmInfos, v:PgmXML())
3654 end
3655
3656 NOTIFY.ALL_PGMS_INFO(MakeXMLNode("pgms", table.concat(AllPgmInfos, "\n")), self._BindingID)
3657end
3658
3659function SecurityPanel:PrxSetTimeDate(TargYear, TargMonth, TargDay, TargHour, TargMinute, TargSecond, InterfaceID)
3660 LogTrace("SecurityPanel.SetTimeDate Date is: %02d/%02d/%d Time is: %02d:%02d:%02d", tonumber(TargMonth), tonumber(TargDay), tonumber(TargYear), tonumber(TargHour), tonumber(TargMinute), tonumber(TargSecond))
3661 SecCom_SendDateAndTime(TargYear, TargMonth, TargDay, TargHour, TargMinute, TargSecond, InterfaceID)
3662end
3663
3664function SecurityPanel:PrxSetPartitionEnabled(PartitionID, Enabled, InterfaceID)
3665 SecCom_SendPartitionEnabled(PartitionID, Enabled, InterfaceID)
3666end
3667
3668function SecurityPanel:PrxSendPgmCommand(PgmID, Command, Time)
3669 if (Command == "Open") then
3670 SecCom_SendPgmControlOpen(PgmID)
3671 elseif (Command == "Close") then
3672 SecCom_SendPgmControlClose(PgmID)
3673 elseif (Command == "Toggle") then
3674 SecCom_SendPgmControlToggle(PgmID)
3675 elseif (Command == "Trigger") then
3676 SecCom_SendPgmControlTrigger(PgmID, Time)
3677 end
3678end
3679
3680function SecurityPanel:PrxSetZoneInfo(ZoneID, ZoneName, ZoneTypeID, DataGuardFlag, InterfaceID)
3681 LogTrace("SecurityPanel.PrxSetZoneInfo Params are %d %s %s", tonumber(ZoneID), tostring(ZoneName), tostring(ZoneTypeID))
3682
3683 local TargZone = ZoneInfoList[ZoneID]
3684 if (TargZone ~= nil) then
3685 TargZone:SetDataGuardFlag(false)
3686 SecCom_SendSetZoneInfo(ZoneID, ZoneName, ZoneTypeID, InterfaceID)
3687 TargZone:SetDataGuardFlag(DataGuardFlag)
3688 else
3689 -- If the proxy is trying to tell us about a zone that we don't have, tell the proxy to get rid of it.
3690 NOTIFY.PANEL_REMOVE_ZONE(ZoneID, self._BindingID)
3691 end
3692end
3693
3694function SecurityPanel:PrxGetZoneInfo(TargZoneID)
3695 LogTrace("SecurityPanel.PrxGetZoneInfo ZoneID[%d]", tonumber(TargZoneID))
3696 return ZoneInfoList[TargZoneID]:ZonePanelXML()
3697end
3698
3699function SecurityPanel:PrxGetPgmState(TargPgmID)
3700 return PgmInfoList[TargPgmID]:PgmXML()
3701end
3702
3703function SecurityPanel:PrxGetAllPgmStates()
3704 local AllPgmInfos = {}
3705
3706 LogTrace("SecurityPanel.GetAllPgmStates")
3707 for k, v in pairs(PgmInfoList) do
3708 table.insert(AllPgmInfos, v:PgmXML())
3709 end
3710
3711 NOTIFY.ALL_PGMS_INFO(MakeXMLNode("pgms", table.concat(AllPgmInfos, "\n")), self._BindingID)
3712end
3713
3714--[[=============================================================================
3715 Functions that are wrappered and meant to be exposed to the driver
3716===============================================================================]]
3717function SecurityPanel:TroubleStart(TroubleStr)
3718 local TroubleID = TroubleStr or self:GetNextTroubleID()
3719
3720 if (TroubleID ~= nil) then
3721 self._TroubleTable[TroubleID] = TroubleStr
3722 LogTrace("SecurityPanel: TroubleStart String is: %s %s", tostring(TroubleStr), tostring(TroubleID))
3723 NOTIFY.TROUBLE_START(TroubleStr, TroubleID, self._BindingID)
3724 end
3725
3726 return TroubleID
3727end
3728
3729function SecurityPanel:TroubleClear(Identifier)
3730
3731 if (Identifier ~= nil) then
3732
3733 if (self._TroubleTable ~= nil) then
3734 self._TroubleTable[Identifier] = nil
3735 end
3736
3737 NOTIFY.TROUBLE_CLEAR(Identifier, self._BindingID)
3738 end
3739end
3740
3741function SecurityPanel:AddZone(ZoneID)
3742 local nZoneID = tonumber(ZoneID)
3743
3744 LogTrace("SecurityPanel.AddZone %d", nZoneID)
3745 if (ZoneInfoList[nZoneID] == nil) then
3746 ZoneInformation:new(nZoneID)
3747 end
3748end
3749
3750function SecurityPanel:RemoveZone(ZoneID)
3751 local nZoneID = tonumber(ZoneID)
3752
3753 LogTrace("SecurityPanel.RemovePanel %d", tonumber(nZoneID))
3754 for k, v in pairs(SecurityPartitionIndexList) do
3755 v:RemoveZone(ZoneID)
3756 end
3757
3758 if (ZoneInfoList[nZoneID] ~= nil) then
3759 ZoneInfoList[nZoneID]:destruct()
3760 end
3761end
3762
3763function SecurityPanel:AddPgm(PgmID)
3764 local nPgmID = tonumber(PgmID)
3765
3766 LogTrace("SecurityPanel.AddPgm %d", nPgmID)
3767 if (PgmInfoList[nPgmID] == nil) then
3768 PgmInformation:new(PgmID)
3769 end
3770end
3771
3772function SecurityPanel:RemovePgm(PgmID)
3773 local nPgmID = tonumber(PgmID)
3774
3775 LogTrace("SecurityPanel.RemovePgm %d", nPgmID)
3776 if (PgmInfoList[nPgmID] ~= nil) then
3777 PgmInfoList[nPgmID]:destruct()
3778 end
3779end
3780
3781function SecurityPanel:RequestAdditionalInfo(Prompt, CurrentInfoStr, FunctionName, MaskData, InterfaceID)
3782 LogTrace("SecurityPanel.RequestAdditionalInfo")
3783 NOTIFY.REQUEST_ADDITIONAL_PANEL_INFO(Prompt, CurrentInfoStr, FunctionName, MaskData, InterfaceID, self._BindingID)
3784end
3785
3786function SecurityPanel:HandleAdditionalInfo(InfoString, NewInfo, FunctionName, InterfaceID)
3787 LogTrace("SecurityPanel.HandleAdditionalInfo")
3788 SecCom_ProcessAdditionalPanelInfo(InfoString, NewInfo, FunctionName, InterfaceID)
3789end
3790
3791function SecurityPanel:SynchronizePanelInfo()
3792 LogTrace("SecurityPanel: SynchronizePanelInfo")
3793 NOTIFY.SYNC_PANEL_INFO(self._BindingID)
3794end
3795
3796function SecurityPanel:ReportPanelInitialized()
3797 LogTrace("SecurityPanel: ReportPanelInitialized")
3798 NOTIFY.PANEL_INITIALIZED(self._BindingID)
3799end end)
3800package.preload['panel_proxy.securitypanel_commands'] = (function (...)
3801--[[=============================================================================
3802 Commands for the SecurityPanel Proxy
3803
3804 Copyright 2016 Control4 Corporation. All Rights Reserved.
3805===============================================================================]]
3806require "common.c4_command"
3807require "common.c4_utils"
3808
3809TEMPLATE_VERSION.securitypanel = "2016.08.01"
3810
3811function PRX_CMD.READ_PANEL_INFO(idBinding, tParams)
3812 if (TheSecurityPanel) then
3813 TheSecurityPanel:PrxReadPanelInfo()
3814 end
3815end
3816
3817function PRX_CMD.GET_PANEL_SETUP(idBinding, tParams)
3818 if (TheSecurityPanel) then
3819 TheSecurityPanel:PrxGetPanelSetup()
3820 end
3821end
3822
3823function PRX_CMD.GET_ALL_PARTITION_INFO(idBinding, tParams)
3824 if (TheSecurityPanel) then
3825 TheSecurityPanel:PrxGetAllPartitionsInfo()
3826 end
3827end
3828
3829function PRX_CMD.GET_ALL_ZONE_INFO(idBinding, tParams)
3830 if (TheSecurityPanel) then
3831 TheSecurityPanel:PrxGetAllZonesInfo()
3832 end
3833end
3834
3835function PRX_CMD.GET_ALL_PGM_INFO(idBinding, tParams)
3836 if (TheSecurityPanel) then
3837 TheSecurityPanel:PrxGetAllPgmsInfo()
3838 end
3839end
3840
3841function PRX_CMD.SET_PANEL_TIME_DATE(idBinding, tParams)
3842 local CurYear = tonumber(tParams.YEAR)
3843 local CurMonth = tonumber(tParams.MONTH)
3844 local CurDay = tonumber(tParams.DAY)
3845 local CurHour = tonumber(tParams.HOUR)
3846 local CurMinute = tonumber(tParams.MINUTE)
3847 local CurSecond = tonumber(tParams.SECOND)
3848 local InterfaceID = tostring(tParams.INTERFACE_ID)
3849
3850 if (TheSecurityPanel) then
3851 TheSecurityPanel:PrxSetTimeDate(CurYear, CurMonth, CurDay, CurHour, CurMinute, CurSecond, InterfaceID)
3852 end
3853end
3854
3855function PRX_CMD.SET_PARTITION_ENABLED(idBinding, tParams)
3856 local PartitionID = tonumber(tParams.PARTITION_ID)
3857 local PartitionEnabled = tParams.ENABLED
3858 local InterfaceID = tParams.INTERFACE_ID
3859
3860 if (TheSecurityPanel) then
3861 TheSecurityPanel:PrxSetPartitionEnabled(PartitionID, PartitionEnabled, InterfaceID)
3862 end
3863end
3864
3865function PRX_CMD.SEND_PGM_COMMAND(idBinding, tParams)
3866 local PgmID = tonumber(tParams.PGM_ID)
3867 local PgmCommand = tParams.COMMAND
3868
3869 if (TheSecurityPanel) then
3870 TheSecurityPanel:PrxSendPgmCommand(PgmID, PgmCommand)
3871 end
3872end
3873
3874function PRX_CMD.SET_ZONE_INFO(idBinding, tParams)
3875 local ZoneID = tonumber(tParams.ZONE_ID)
3876 local ZoneName = tParams.NAME
3877 local ZoneTypeID = tonumber(tParams.TYPE_ID)
3878 local DataGuarded = toboolean(tParams.DATA_GUARDED)
3879 local InterfaceID = tParams.INTERFACE_ID
3880
3881 if (TheSecurityPanel) then
3882 TheSecurityPanel:PrxSetZoneInfo(ZoneID, ZoneName, ZoneTypeID, DataGuarded, InterfaceID)
3883 end
3884end
3885
3886function PRX_CMD.ADDITIONAL_PANEL_INFO(idBinding, tParams)
3887 local InfoString = tParams.INFO_STRING
3888 local NewInfo = tParams.NEW_INFO
3889 local FunctionName = tParams.FUNCTION_NAME
3890 local InterfaceID = tParams.INTERFACE_ID
3891
3892 if (TheSecurityPanel) then
3893 TheSecurityPanel:HandleAdditionalInfo(InfoString, NewInfo, FunctionName, InterfaceID)
3894 end
3895end
3896 end)
3897package.preload['panel_proxy.securitypanel_functions'] = (function (...)
3898--[[=============================================================================
3899 Functions dealing with the management of panel information, zones and their
3900 states, as well as pgms and their states
3901
3902 Copyright 2015 Control4 Corporation. All Rights Reserved.
3903===============================================================================]]
3904TEMPLATE_VERSION.securitypanel = "6"
3905
3906--[[=============================================================================
3907 IsPgmValid(PgmID)
3908
3909 Description:
3910 Identifies whether or not the given PgmID has been added to the system
3911
3912 Parameters:
3913 PgmID(int) - The number for the pgm in question
3914
3915 Returns:
3916 A boolean indicating the validity of the specified pgm
3917===============================================================================]]
3918function IsPgmValid(PgmID)
3919 return (PgmInfoList[tonumber(PgmID)] ~= nil)
3920end
3921
3922--[[=============================================================================
3923 SetPgmState(PgmID, IsOpen, Initializing)
3924
3925 Description:
3926 Sets the state of the specified pgm
3927
3928 Parameters:
3929 PgmID(int) - The number for the pgm whose state is being set
3930 IsOpen(bool) - Indicates the state of the specified pgm
3931 Initializing(bool) - Indicates whether this is the initialization of the
3932 pgm. If true then the programming events within the
3933 system will not be fired.
3934
3935 Returns:
3936 None
3937===============================================================================]]
3938function SetPgmState(PgmID, IsOpen, Initializing)
3939 if (not IsPgmValid(PgmID)) then
3940 AddPgm(PgmID)
3941 end
3942
3943 PgmInfoList[PgmID]:SetPgmState(IsOpen, Initializing)
3944end
3945
3946--[[=============================================================================
3947 IsPgmOpen(PgmID)
3948
3949 Description:
3950 Identifies whether or not the specified zone is open
3951
3952 Parameters:
3953 PgmID(int) - The number for the pgm whose open state is in question
3954
3955 Returns:
3956 A boolean indicating the open state of the given pgm
3957===============================================================================]]
3958function IsPgmOpen(PgmID)
3959 if (not IsPgmValid(PgmID)) then
3960 return false
3961 end
3962
3963 return PgmInfoList[PgmID]:GetPgmState()
3964end
3965
3966--[[=============================================================================
3967 AddPgm(PgmID)
3968
3969 Description:
3970 Adds the specified Pgm to the managed list
3971
3972 Parameters:
3973 PgmID(int) - The number of the pgm that is being added
3974===============================================================================]]
3975function AddPgm(PgmID)
3976 TheSecurityPanel:AddPgm(PgmID)
3977end
3978
3979--[[=============================================================================
3980 RemovePgm(PgmID)
3981
3982 Description:
3983 Removes the specified Pgm from the managed list
3984
3985 Parameters:
3986 PgmID(int) - The number of the pgm that is being removed
3987===============================================================================]]
3988function RemovePgm(PgmID)
3989 TheSecurityPanel:RemovePgm(PgmID)
3990end
3991
3992--[[=============================================================================
3993 SetDefaultZoneName(ZoneName)
3994
3995 Description:
3996 Set the default label for the zone in the system, when no label is given
3997
3998 Parameters:
3999 ZoneName(string) - The label to use for unnamed zones
4000
4001 Returns:
4002 None
4003===============================================================================]]
4004function SetDefaultZoneName(ZoneName)
4005 DefaultZoneName = tostring(ZoneName)
4006end
4007
4008--[[=============================================================================
4009 IsZoneValid(ZoneID)
4010
4011 Description:
4012 Identifies whether or not the given ZoneID has been added to the system
4013
4014 Parameters:
4015 ZoneID(int) - The number for the zone in question
4016
4017 Returns:
4018 A boolean indicating the validity of the specified zone
4019===============================================================================]]
4020function IsZoneValid(ZoneID)
4021 return (ZoneInfoList[tonumber(ZoneID)] ~= nil)
4022end
4023
4024--[[=============================================================================
4025 GetTotalPgmCount()
4026
4027 Description:
4028 Gets the current number of PGMs that have been created by the driver
4029
4030 Returns:
4031 Returns the current number of PGMs that have been created by the driver
4032===============================================================================]]
4033function GetTotalPgmCount()
4034 return #PgmInfoList
4035end
4036
4037--[[=============================================================================
4038 GetTotalZoneCount()
4039
4040 Description:
4041 Gets the current number of zones that have been created by the driver
4042
4043 Returns:
4044 Returns the current number of zones that havee been created by the driver
4045===============================================================================]]
4046function GetTotalZoneCount()
4047 return #ZoneInfoList
4048end
4049
4050--[[=============================================================================
4051 IsZoneBypassed(ZoneID)
4052
4053 Description:
4054 Identifies whether or not the specified zone id has been bypassed
4055
4056 Parameters:
4057 ZoneID(int) - The number for the zone whose bypass state is in question
4058
4059 Returns:
4060 A boolean indicating the bypass state of the given zone
4061===============================================================================]]
4062function IsZoneBypassed(ZoneID)
4063 if (IsZoneValid(ZoneID)) then
4064 return ZoneInfoList[tonumber(ZoneID)]:IsBypassed()
4065 else
4066 return false
4067 end
4068end
4069
4070--[[=============================================================================
4071 IsZoneOpen(ZoneID)
4072
4073 Description:
4074 Identifies whether or not the specified zone is open
4075
4076 Parameters:
4077 ZoneID(int) - The number for the zone whose open state is in question
4078
4079 Returns:
4080 A boolean indicating the open state of the given zone
4081===============================================================================]]
4082function IsZoneOpen(ZoneID)
4083 if (IsZoneValid(ZoneID)) then
4084 return ZoneInfoList[tonumber(ZoneID)]:IsOpen()
4085 else
4086 return false
4087 end
4088end
4089
4090--[[=============================================================================
4091 GetZoneType(ZoneID)
4092
4093 Description:
4094 Identifies the type of the zone specified
4095
4096 Parameters:
4097 ZoneID(int) - The number for the zone whose type is in question
4098
4099 Returns:
4100 The panels zone type
4101===============================================================================]]
4102function GetZoneType(ZoneID)
4103 if (IsZoneValid(ZoneID)) then
4104 return ZoneInfoList[tonumber(ZoneID)]:GetZoneType()
4105 else
4106 return 0
4107 end
4108end
4109
4110--[[=============================================================================
4111 SetZoneInfo(ZoneID, ZoneName, ZoneTypeID, ZoneTypeID_C4)
4112
4113 Description:
4114 Set the details for the specified zones
4115
4116 Parameters:
4117 ZoneID(int) - The number for the zone whose state is being set
4118 ZoneName(string) - The identifying label of the zone
4119 ZoneTypeID(int) - The zone type from the perspective of the vendor
4120 C4ZoneTypeID(int) - The control4 zone type that maps the vendor specific
4121 type with the control4 sensor types.
4122
4123 Control4 Zone Types
4124 ==========================
4125 Unknown = 0
4126 Contact Sensor = 1
4127 Exterior Door = 2
4128 Exterior Window = 3
4129 Interior Door = 4
4130 Motion Sensor = 5
4131 Fire = 6
4132 Gas = 7
4133 Carbon Monoxide = 8
4134 Heat = 9
4135 Water = 10
4136 Smoke = 11
4137 Pressure = 12
4138 Glass Break = 13
4139 Gate = 14
4140 Garage Door = 15
4141===============================================================================]]
4142function SetZoneInfo(ZoneID, ZoneName, ZoneTypeID, ZoneTypeID_C4)
4143 local nZoneID = tonumber(ZoneID)
4144
4145 if (not IsZoneValid(nZoneID)) then
4146 AddZone(nZoneID)
4147 end
4148
4149 return ZoneInfoList[nZoneID]:SetZoneInfo(ZoneName, ZoneTypeID, ZoneTypeID_C4)
4150end
4151
4152--[[=============================================================================
4153 AddZone(ZoneID)
4154
4155 Description:
4156 Adds the specified zone number to the list of managed/monitored zones
4157
4158 Parameters:
4159 ZoneID(int) - The number for the zone that is being added
4160===============================================================================]]
4161function AddZone(ZoneID)
4162 TheSecurityPanel:AddZone(ZoneID)
4163end
4164
4165--[[=============================================================================
4166 RemoveZone(ZoneID)
4167
4168 Description:
4169 Removes the specified zone number from the list of managed/monitored zones
4170
4171 Parameters:
4172 ZoneID(int) - The number for the zone that is being removed
4173===============================================================================]]
4174function RemoveZone(ZoneID)
4175 TheSecurityPanel:RemoveZone(ZoneID)
4176end
4177
4178--[[=============================================================================
4179 SetZoneState(ZoneID, IsOpen, Initializing)
4180
4181 Description:
4182 Sets the specified zones state with the system
4183
4184 Parameters:
4185 ZoneID(int) - The number for the zone whose state is being set
4186 IsOpen(bool) - Indicates the state of the specified zone
4187 Initializing(bool) - Indicates whether this is the initialization of the
4188 zone. If true then the programming events within the
4189 system will not be fired.
4190
4191 Returns:
4192 True if something changed, otherwise False
4193===============================================================================]]
4194function SetZoneState(ZoneID, IsOpen, Initializing)
4195 if (ZoneInfoList[tonumber(ZoneID)] ~= nil) then
4196 return ZoneInfoList[tonumber(ZoneID)]:SetZoneState(IsOpen, Initializing)
4197 else
4198 return false
4199 end
4200end
4201
4202--[[=============================================================================
4203 SetZoneBypassState(ZoneID, IsBypassed, Initializing)
4204
4205 Description:
4206 Sets the specified zones bypass state with the system
4207
4208 Parameters:
4209 ZoneID(int) - The number for the zone whose state is being set
4210 IsBypassed(bool) - Indicates whether the zone has been bypassed for the
4211 specified zone
4212 Initializing(bool) - Indicates whether this is the initialization of the
4213 zone. If true then the programming events within the
4214 system will not be fired.
4215
4216 Returns:
4217 True if something changed, otherwise False
4218===============================================================================]]
4219function SetZoneBypassState(ZoneID, IsBypassed, Initializing)
4220 if (ZoneInfoList[tonumber(ZoneID)] ~= nil) then
4221 return ZoneInfoList[tonumber(ZoneID)]:SetBypassState(IsBypassed, Initializing)
4222 else
4223 return false
4224 end
4225end
4226
4227--[[=============================================================================
4228 StartTroubleCondition(TroubleMessage)
4229
4230 Description:
4231 Sets the given string as a trouble condition with the panel
4232
4233 Parameters:
4234 TroubleMessage(string) - The trouble condition to set for the panel
4235
4236 Returns:
4237 An identifier to uniquely identify this trouble condition with the panel.
4238===============================================================================]]
4239function StartTroubleCondition(TroubleMessage)
4240 return TheSecurityPanel:TroubleStart(TroubleMessage)
4241end
4242
4243--[[=============================================================================
4244 ClearTroubleCondition(Identifier)
4245
4246 Description:
4247 Clears the trouble condition with the panel
4248
4249 Parameters:
4250 Identifier(string) - An identifier to uniquely identify this trouble
4251 condition with the panel.
4252===============================================================================]]
4253function ClearTroubleCondition(Identifier)
4254 TheSecurityPanel:TroubleClear(Identifier)
4255end
4256
4257--[[=============================================================================
4258 function PanelRequestAdditionalInfo(Prompt, CurrentInfoStr, FunctionName, MaskData, InterfaceID)
4259
4260 Description:
4261 Provides a mechanism to ask the UI for the security panel to provide more
4262 info, the most common use case would be if a user code or an installers code
4263 would be required to complete a desired action.
4264
4265 Parameters:
4266 Prompt(string) - The prompt that the UI will display when asking
4267 for the additional information
4268 CurrentInfoString(string) - A string of current information that will be
4269 passed along as the new information is requested
4270 as to what the driver should do with the new
4271 information. Usually it would indicate which
4272 routine should be called and what parameters
4273 should be passed to that routine.
4274 FunctionName(string) - Optional name of a function that the handling
4275 routine should call when the additional info
4276 is provided
4277 MaskData(boolean) - Indicates whether or not the UI should hide the
4278 data (i.e. just print asterisks for letters) as
4279 the requested data is entered
4280 InterfaceID(string) - Unique id of the interface that originally made
4281 the request
4282===============================================================================]]
4283function PanelRequestAdditionalInfo(Prompt, CurrentInfoStr, FunctionName, MaskData, InterfaceID)
4284 TheSecurityPanel:RequestAdditionalInfo(Prompt, CurrentInfoStr, FunctionName, MaskData, InterfaceID)
4285end
4286
4287--[[=============================================================================
4288 SynchronizePanelInfo()
4289
4290 Description:
4291 Notifies the proxy that the driver would like to resynch the panels zone
4292 information with that retained by the proxy. This allows to identify when the
4293 proxy is out of synch with the driver and do some house cleaning
4294===============================================================================]]
4295function SynchronizePanelInfo()
4296 TheSecurityPanel:SynchronizePanelInfo()
4297end
4298
4299--[[=============================================================================
4300 ReportPanelInitialized()
4301
4302 Description:
4303 Notifies the proxy that the driver is fully initialized and ready to receive
4304 information and synchronize if needed.
4305===============================================================================]]
4306function ReportPanelInitialized()
4307 TheSecurityPanel:ReportPanelInitialized()
4308end
4309
4310 end)
4311package.preload['panel_proxy.securitypanel_notifies'] = (function (...)
4312--[[=============================================================================
4313 Notifies for the SecurityPanel Proxy
4314
4315 Copyright 2015 Control4 Corporation. All Rights Reserved.
4316===============================================================================]]
4317require "common.c4_notify"
4318
4319TEMPLATE_VERSION.securitypanel = "6"
4320
4321function NOTIFY.PANEL_ZONE_STATE(ZoneID, IsOpen, Initializing, BindingID)
4322 local ZoneParams = {}
4323
4324 LogTrace("NOTIFY.PANEL_ZONE_STATE: %d %s %s %d", tonumber(ZoneID), tostring(IsOpen), tostring(Initializing), tonumber(BindingID))
4325 ZoneParams["ZONE_ID"] = ZoneID
4326 ZoneParams["ZONE_OPEN"] = IsOpen
4327 ZoneParams["INITIALIZING"] = Initializing
4328
4329 SendNotify("PANEL_ZONE_STATE", ZoneParams, BindingID)
4330end
4331
4332function NOTIFY.PANEL_PGM_STATE(PgmID, IsOpen, Initializing, BindingID)
4333 local PgmParams = {}
4334
4335 LogTrace("NOTIFY.PANEL_PGM_STATE: %d %s %d", tonumber(PgmID), tostring(IsOpen), tonumber(BindingID))
4336 PgmParams["PGM_ID"] = PgmID
4337 PgmParams["PGM_OPEN"] = IsOpen
4338 PgmParams["INITIALIZING"] = Initializing
4339
4340 SendNotify("PANEL_PGM_STATE", PgmParams, BindingID)
4341end
4342
4343function NOTIFY.PANEL_ADD_PGM(PgmID, BindingID)
4344 local AddPgmParams = {}
4345
4346 LogTrace("NOTIFY.PANEL_ADD_PGM: %d %d", tonumber(PgmID), tonumber(BindingID))
4347 AddPgmParams["ID"] = PgmID
4348
4349 SendNotify("PANEL_ADD_PGM", AddPgmParams, BindingID)
4350end
4351
4352function NOTIFY.PANEL_REMOVE_PGM(PgmID, BindingID)
4353 local RemovePgmParams = {}
4354
4355 LogTrace("NOTIFY.PANEL_REMOVE_PGM: %d %d", tonumber(PgmID), tonumber(BindingID))
4356 RemovePgmParams["ID"] = PgmID
4357
4358 SendNotify("PANEL_REMOVE_PGM", RemovePgmParams, BindingID)
4359end
4360
4361function NOTIFY.PANEL_PARTITION_STATE(PartitionID, PartitionState, StateType, BindingID)
4362 local PartitionParams = {}
4363
4364 LogTrace("NOTIFY.PANEL_PARTITION_STATE: %d %s %s %d", tonumber(PartitionID), tostring(PartitionState), tostring(StateType), tonumber(BindingID))
4365 PartitionParams["PARTITION_ID"] = PartitionID
4366 PartitionParams["STATE"] = PartitionState
4367 PartitionParams["TYPE"] = StateType
4368
4369 SendNotify("PANEL_PARTITION_STATE", PartitionParams, BindingID)
4370end
4371
4372function NOTIFY.PANEL_ZONE_INFO(ZoneID, ZoneName, ZoneTypeID, Partitions, IsOpen, BindingID)
4373 local ZoneInfoParams = {}
4374
4375 LogTrace("NOTIFY.PANEL_ZONE_INFO: %d %s %d %d", tonumber(ZoneID), tostring(ZoneName), tonumber(ZoneTypeID), tonumber(BindingID))
4376 ZoneInfoParams["ID"] = ZoneID
4377 ZoneInfoParams["NAME"] = ZoneName
4378 ZoneInfoParams["TYPE_ID"] = ZoneTypeID
4379 ZoneInfoParams["PARTITIONS"] = Partitions
4380 ZoneInfoParams["IS_OPEN"] = tostring(IsOpen)
4381
4382 SendNotify("PANEL_ZONE_INFO", ZoneInfoParams, BindingID)
4383end
4384
4385function NOTIFY.PANEL_REMOVE_ZONE(ZoneID, BindingID)
4386 local RemoveZoneParams = {}
4387
4388 LogTrace("NOTIFY.PANEL_REMOVE_ZONE: %d %d", tonumber(ZoneID), tonumber(BindingID))
4389 RemoveZoneParams["ID"] = ZoneID
4390
4391 SendNotify("PANEL_REMOVE_ZONE", RemoveZoneParams, BindingID)
4392end
4393
4394function NOTIFY.TROUBLE_START(TroubleText, Identifier, BindingID)
4395 local TroubleParams = {}
4396
4397 LogTrace("NOTIFY.TROUBLE_START: %s %s %d", tostring(TroubleText), tostring(Identifier), tonumber(BindingID))
4398 TroubleParams["TROUBLE_TEXT"] = TroubleText
4399 TroubleParams["IDENTIFIER"] = Identifier
4400
4401 SendNotify("TROUBLE_START", TroubleParams, BindingID)
4402end
4403
4404function NOTIFY.TROUBLE_CLEAR(Identifier, BindingID)
4405 local TroubleParams = {}
4406
4407 LogTrace("NOTIFY.TROUBLE_CLEAR: %s %d", tostring(Identifier), tonumber(BindingID))
4408 TroubleParams["IDENTIFIER"] = Identifier
4409
4410 SendNotify("TROUBLE_CLEAR", TroubleParams, BindingID)
4411end
4412
4413function NOTIFY.ALL_PARTITIONS_INFO(InfoStr, BindingID)
4414 LogTrace("NOTIFY.ALL_PARTITIONS_INFO: %s", InfoStr)
4415 SendNotify("ALL_PARTITIONS_INFO", InfoStr, BindingID)
4416end
4417
4418function NOTIFY.ALL_ZONES_INFO(InfoStr, BindingID)
4419 LogTrace("NOTIFY.ALL_ZONES_INFO: %s", InfoStr)
4420 SendNotify("ALL_ZONES_INFO", InfoStr, BindingID)
4421end
4422
4423function NOTIFY.ALL_PGMS_INFO(InfoStr, BindingID)
4424 LogTrace("NOTIFY.ALL_PGMS_INFO: %s", InfoStr)
4425 SendNotify("ALL_PGMS_INFO", InfoStr, BindingID)
4426end
4427
4428function NOTIFY.REQUEST_ADDITIONAL_PANEL_INFO(Prompt, InfoString, FunctionName, MaskData, InterfaceID, BindingID)
4429 local ParmList = {}
4430
4431 LogTrace("NOTIFY.REQUEST_ADDITIONAL_PANEL_INFO: %s %s %d", tostring(Prompt), InfoString, tonumber(BindingID))
4432 ParmList["PROMPT"] = Prompt
4433 ParmList["INFO_STRING"] = InfoString
4434 ParmList["FUNCTION_NAME"] = FunctionName
4435 ParmList["MASK_DATA"] = tostring(MaskData)
4436 ParmList["INTERFACE_ID"] = tostring(InterfaceID)
4437
4438 SendNotify("REQUEST_ADDITIONAL_PANEL_INFO", ParmList, BindingID)
4439end
4440
4441function NOTIFY.SYNC_PANEL_INFO(BindingID)
4442 LogTrace("NOTIFY.SYNC_PANEL_INFO")
4443 SendNotify("SYNC_PANEL_INFO", {}, BindingID)
4444end
4445
4446function NOTIFY.PANEL_INITIALIZED(BindingID)
4447 LogTrace("NOTIFY.PANEL_INITIALIZED")
4448 SendNotify("PANEL_INITIALIZED", {}, BindingID)
4449end
4450
4451 end)
4452package.preload['panel_proxy.zone_info'] = (function (...)
4453--[[=============================================================================
4454 ZoneInformation Class
4455
4456 Copyright 2016 Control4 Corporation. All Rights Reserved.
4457===============================================================================]]
4458require "lib.c4_object"
4459
4460TEMPLATE_VERSION.securitypanel = "2016.08.01"
4461
4462-- Control4 zone type mapping
4463ZoneTypes = {
4464 -- Name to ID mappings
4465 UNKNOWN = 0,
4466 CONTACT_SENSOR = 1,
4467 EXTERIOR_DOOR = 2,
4468 EXTERIOR_WINDOW = 3,
4469 INTERIOR_DOOR = 4,
4470 MOTION_SENSOR = 5,
4471 FIRE = 6,
4472 GAS = 7,
4473 CARBON_MONOXIDE = 8,
4474 HEAT = 9,
4475 WATER = 10,
4476 SMOKE = 11,
4477 PRESSURE = 12,
4478 GLASS_BREAK = 13,
4479 GATE = 14,
4480 GARAGE_DOOR = 15,
4481 TYPES_COUNT = 16, -- The number of defined values
4482 -- ID to Name mappings
4483 [0] = "UNKNOWN",
4484 "CONTACT_SENSOR",
4485 "EXTERIOR_DOOR",
4486 "EXTERIOR_WINDOW",
4487 "INTERIOR_DOOR",
4488 "MOTION_SENSOR",
4489 "FIRE",
4490 "GAS",
4491 "CARBON_MONOXIDE",
4492 "HEAT",
4493 "WATER",
4494 "SMOKE",
4495 "PRESSURE",
4496 "GLASS_BREAK",
4497 "GATE",
4498 "GARAGE_DOOR" }
4499
4500ZoneInfoList = {}
4501ZoneInformation = inheritsFrom(nil)
4502DefaultZoneName = "Zone "
4503
4504--[[=============================================================================
4505 Functions that are meant to be private to the class
4506===============================================================================]]
4507function ZoneInformation:construct(ZoneID)
4508
4509 self._ZoneID = ZoneID
4510 self._IsOpen = false
4511 self._IsBypassed = false
4512 self._ZoneName = DefaultZoneName .. tostring(ZoneID)
4513 self._ZoneTypeID_C4 = 0 -- Control4 zone type
4514 self._ZoneTypeID_3P = 0 -- Security panel zone type
4515 self._NeedToSendInitialInfo = true
4516
4517 self._PartitionMemberList = {}
4518
4519 ZoneInfoList[ZoneID] = self
4520
4521 self._DataGuarded = false
4522end
4523
4524function ZoneInformation:destruct()
4525 NOTIFY.PANEL_REMOVE_ZONE(self._ZoneID, TheSecurityPanel._BindingID)
4526 ZoneInfoList[self._ZoneID] = nil
4527end
4528
4529function ZoneInformation:ZonePanelXML()
4530 local ZoneXMLInfo = {}
4531
4532 table.insert(ZoneXMLInfo, MakeXMLNode("id", tostring(self._ZoneID)))
4533 table.insert(ZoneXMLInfo, MakeXMLNode("name", tostring(self._ZoneName)))
4534 table.insert(ZoneXMLInfo, MakeXMLNode("type_id", tostring(self._ZoneTypeID_C4)))
4535 table.insert(ZoneXMLInfo, MakeXMLNode("partitions", self:ListPartitions()))
4536 table.insert(ZoneXMLInfo, MakeXMLNode("can_bypass", "true"))
4537 table.insert(ZoneXMLInfo, MakeXMLNode("is_open", tostring(self._IsOpen)))
4538
4539 return MakeXMLNode("zone", table.concat(ZoneXMLInfo, "\n"))
4540end
4541
4542function ZoneInformation:ZonePartitionXML()
4543 return MakeXMLNode("zone", MakeXMLNode("id", tostring(self._ZoneID)))
4544end
4545
4546function ZoneInformation:ListPartitions()
4547 local PartitionList = ""
4548
4549 for i, p in pairs(self._PartitionMemberList) do
4550 if (PartitionList ~= "") then
4551 PartitionList = (PartitionList .. ",")
4552 end
4553
4554 PartitionList = PartitionList .. tostring(i)
4555 end
4556
4557 return (PartitionList ~= "") and PartitionList or " "
4558end
4559
4560function ZoneInformation:ZoneInfoChanged()
4561 LogDebug("ZoneInfoChanged => Zone %d: Name = %s, Type = %d, Type_Alt = %d", tonumber(self._ZoneID), tostring(self._ZoneName), tonumber(self._ZoneTypeID_C4), tonumber(self._ZoneTypeID_3P))
4562 NOTIFY.PANEL_ZONE_INFO(self._ZoneID, self._ZoneName, self._ZoneTypeID_C4, self:ListPartitions(), self._IsOpen, TheSecurityPanel._BindingID)
4563end
4564
4565--[[=============================================================================
4566 Functions that are wrappered and meant to be exposed to the driver
4567===============================================================================]]
4568function ZoneInformation:IsBypassed()
4569 return self._IsBypassed
4570end
4571
4572function ZoneInformation:IsOpen()
4573 return self._IsOpen
4574end
4575
4576function ZoneInformation:GetZoneType()
4577 return self._ZoneTypeID_3P
4578end
4579
4580function ZoneInformation:GetZoneState()
4581 return self._IsOpen and "OPENED" or "CLOSED"
4582end
4583
4584function ZoneInformation:SetDataGuardFlag(GuardIt)
4585 self._DataGuarded = GuardIt
4586 LogTrace("ZoneInformation:SetDataGuardFlag for Zone %d to %s", self._ZoneID, tostring(self._DataGuarded))
4587end
4588
4589function ZoneInformation:SetZoneInfo(ZoneName, ZoneTypeID, ZoneTypeID_C4)
4590 local SomethingChanged = false
4591
4592 if(not self._DataGuarded) then
4593 if (self._ZoneName ~= ZoneName and ZoneName ~= "") then
4594 LogTrace("Changing name for zone %d from %s to %s", self._ZoneID, self._ZoneName, ZoneName)
4595 self._ZoneName = ZoneName
4596 SomethingChanged = true
4597 end
4598
4599 if (self._ZoneTypeID_C4 ~= ZoneTypeID_C4 and ZoneTypeID_C4 ~= "") then
4600 self._ZoneTypeID_C4 = ZoneTypeID_C4
4601 SomethingChanged = true
4602 end
4603 end
4604
4605 if (self._ZoneTypeID_3P ~= ZoneTypeID and ZoneTypeID ~= "") then
4606 self._ZoneTypeID_3P = ZoneTypeID
4607 end
4608
4609 if(SomethingChanged) then
4610 self:ZoneInfoChanged()
4611 end
4612end
4613
4614function ZoneInformation:AddToPartition(PartitionID)
4615 self._PartitionMemberList[PartitionID] = SecurityPartitionIndexList[PartitionID]
4616end
4617
4618function ZoneInformation:RemoveFromPartition(PartitionID)
4619 self._PartitionMemberList[PartitionID] = nil
4620end
4621
4622function ZoneInformation:SetZoneState(IsOpen, Initializing)
4623 local RetVal = false -- return true if the state changed, false if it didn't
4624 local JustInitializing = Initializing or false
4625
4626 if ((self._IsOpen ~= IsOpen) or self._NeedToSendInitialInfo or JustInitializing) then
4627 self._IsOpen = IsOpen
4628 RetVal = true
4629
4630 LogDebug("!!!!!! Zone %d %s !!!!!!", tonumber(self._ZoneID), tostring(self:GetZoneState()))
4631 for k, CurPartition in pairs(self._PartitionMemberList) do
4632 NOTIFY.ZONE_STATE(self._ZoneID, self._IsOpen, self._IsBypassed, CurPartition._BindingID)
4633 end
4634
4635 NOTIFY.PANEL_ZONE_STATE(self._ZoneID, self._IsOpen, self._NeedToSendInitialInfo or JustInitializing, TheSecurityPanel._BindingID)
4636 self._NeedToSendInitialInfo = false
4637 end
4638
4639 return RetVal
4640end
4641
4642function ZoneInformation:SetBypassState(IsBypassed, Initializing)
4643 local RetVal = false -- return true if the state changed, false if it didn't
4644 local JustInitializing = Initializing or false
4645
4646 if (self._IsBypassed ~= IsBypassed) then
4647
4648 RetVal = true
4649 self._IsBypassed = IsBypassed
4650
4651 if (not JustInitializing) then
4652
4653 for k, CurPartition in pairs(self._PartitionMemberList) do
4654 NOTIFY.ZONE_STATE(self._ZoneID, self._IsOpen, self._IsBypassed, CurPartition._BindingID)
4655 end
4656 end
4657 end
4658
4659 return RetVal
4660end end)
4661package.preload['partition_proxy.securitypartition'] = (function (...)
4662--[[=============================================================================
4663 SecurityPartition Class
4664
4665 Copyright 2016 Control4 Corporation. All Rights Reserved.
4666===============================================================================]]
4667require "common.c4_utils"
4668require "lib.c4_object"
4669require "lib.c4_log"
4670require "partition_proxy.securitypartition_functions"
4671require "partition_proxy.securitypartition_commands"
4672require "partition_proxy.securitypartition_notifies"
4673require "panel_proxy.zone_info"
4674
4675TEMPLATE_VERSION.securitypartition = "2016.07.05"
4676
4677SecurityPartitionIndexList = {}
4678SecurityPartitionFunctions = {}
4679SecurityPartitionBindingList = {}
4680
4681--[[=============================================================================
4682 ARMED STATES
4683===============================================================================]]
4684AS_ARMED = "ARMED"
4685AS_ALARM = "ALARM"
4686AS_OFFLINE = "OFFLINE"
4687AS_EXIT_DELAY = "EXIT_DELAY"
4688AS_ENTRY_DELAY = "ENTRY_DELAY"
4689AS_DISARMED_READY = "DISARMED_READY"
4690AS_DISARMED_NOT_READY = "DISARMED_NOT_READY"
4691AS_CONFIRMATION_REQUIRED = "CONFIRMATION_REQUIRED"
4692
4693SecurityPartition = inheritsFrom(nil)
4694
4695--[[=============================================================================
4696 Functions that are meant to be private to the class
4697===============================================================================]]
4698function SecurityPartition:construct(PartitionNumber, BindingID)
4699
4700 self._IsEnabled = false
4701 self._PartitionNumber = PartitionNumber
4702 self._BindingID = BindingID
4703 self._CurrentPartitionState = "Unknown"
4704 self._InitializingStatus = true
4705 self._MyZoneList = {}
4706
4707 self._DelayTimeTotal = 0
4708 self._DelayTimeRemaining = 0
4709 self._OpenZoneCount = 0
4710
4711 self._CodeRequiredToArm = false
4712 self._CodeRequiredToClear = true
4713 self._InAlarm = false
4714 self._CurrentStateType = ""
4715 self._DefaultUserCode = ""
4716
4717 SecurityPartitionIndexList[PartitionNumber] = self
4718 SecurityPartitionBindingList[BindingID] = self
4719end
4720
4721function SecurityPartition:InitialSetup()
4722end
4723
4724function SecurityPartition:SetInitializingFlag(FlagValue)
4725 self._InitializingStatus = FlagValue
4726end
4727
4728function SetAllPartitionsOffline()
4729
4730 LogTrace("SetAllPartitionsOffline")
4731 for _, CurPartition in pairs(SecurityPartitionIndexList) do
4732 if(CurPartition:IsEnabled()) then
4733 CurPartition:SetPartitionState(AS_OFFLINE, "")
4734 end
4735 end
4736end
4737
4738function SecurityPartition:EntryExitDelay(DelayType, DelayActive, TotalTime, RemainingTime)
4739 local DelayMessage = DelayType .. " Delay"
4740 local OldTotalTime = self._DelayTimeTotal
4741 local OldRemainingTime = self._DelayTimeRemaining
4742
4743 if (DelayActive) then
4744 -- Delay On
4745 if (TotalTime == 0) then
4746 TotalTime = OldTotalTime
4747 end
4748
4749 self._DelayTimeTotal = (TotalTime > RemainingTime) and TotalTime or RemainingTime
4750 self._DelayTimeRemaining = RemainingTime
4751 DelayMessage = DelayMessage .. " On"
4752 else
4753 -- Delay Off
4754 self._DelayTimeTotal = 0
4755 self._DelayTimeRemaining = 0
4756 DelayMessage = DelayMessage .. " Off"
4757 end
4758
4759 LogDebug("EntryExitDelay: %s", DelayMessage)
4760end
4761
4762function SecurityPartition:PartitionXML()
4763 local PartitionXMLInfo = {}
4764
4765 table.insert(PartitionXMLInfo, MakeXMLNode("id", tostring(self._PartitionNumber)))
4766 table.insert(PartitionXMLInfo, MakeXMLNode("enabled", tostring(self._IsEnabled)))
4767 table.insert(PartitionXMLInfo, MakeXMLNode("binding_id", tostring(self._BindingID)))
4768 table.insert(PartitionXMLInfo, MakeXMLAttrNode("state", tostring(self._CurrentPartitionState), "type", tostring(self._CurrentStateType)))
4769
4770 return MakeXMLNode("partition", table.concat(PartitionXMLInfo, "\n"))
4771end
4772
4773function SecurityPartition:NotifyPartitionState()
4774
4775 LogTrace("Partition %d set to partition state %s : %s Alarm is %s", tonumber(self._PartitionNumber), tostring(self._CurrentPartitionState), tostring(self._CurrentStateType), tostring(self._InAlarm))
4776 if (self._InitializingStatus) then
4777 NOTIFY.PARTITION_STATE_INIT(self._CurrentPartitionState, self._CurrentStateType, self._DelayTimeTotal, self._DelayTimeRemaining, self._CodeRequiredToClear, self._BindingID)
4778 else
4779 NOTIFY.PARTITION_STATE(self._CurrentPartitionState, self._CurrentStateType, self._DelayTimeTotal, self._DelayTimeRemaining, self._CodeRequiredToClear, self._BindingID)
4780 end
4781
4782 NOTIFY.PANEL_PARTITION_STATE(self._PartitionNumber, self._CurrentPartitionState, self._CurrentStateType, PANEL_PROXY_BINDINGID)
4783end
4784
4785function SecurityPartition:ClearDisplayText()
4786 NOTIFY.DISPLAY_TEXT("", self._BindingID)
4787end
4788
4789--[[=============================================================================
4790 Functions for handling request from the Partition Proxy
4791===============================================================================]]
4792function SecurityPartition:PrxGetCurrentState(tParams)
4793 LogTrace("GetCurrentState for partition %d", tonumber(self._PartitionNumber))
4794 NOTIFY.PARTITION_STATE_INIT(self._CurrentPartitionState, self._CurrentStateType, self._DelayTimeTotal, self._DelayTimeRemaining, self._CodeRequiredToClear, self._BindingID)
4795end
4796
4797function SecurityPartition:PrxPartitionArm(tParams)
4798 local ArmType = tParams["ArmType"]
4799 local UserCode = tParams["UserCode"]
4800 local Bypass = toboolean(tParams["Bypass"])
4801 local InterfaceID = tParams["InterfaceID"] or ""
4802
4803 if (InterfaceID == "DirectorProgramming") then
4804 UserCode = self:GetDefaultUserCode()
4805 end
4806
4807 LogTrace("PrxPartitionArm %d %s %s %s", tonumber(self._PartitionNumber), tostring(ArmType), tostring(UserCode), tostring(Bypass))
4808 SecCom_SendArmPartition(self._PartitionNumber, ArmType, UserCode, Bypass, InterfaceID)
4809end
4810
4811function SecurityPartition:PrxPartitionDisarm(tParams)
4812 local UserCode = tParams["UserCode"]
4813 local InterfaceID = tParams["InterfaceID"] or ""
4814
4815 if (InterfaceID == "DirectorProgramming") then
4816 UserCode = self:GetDefaultUserCode()
4817 end
4818
4819 LogTrace("PartitionDisarm")
4820 SecCom_SendDisarmPartition(self._PartitionNumber, UserCode, InterfaceID)
4821end
4822
4823function SecurityPartition:PrxArmCancel(tParams)
4824 local InterfaceID = tParams["InterfaceID"] or ""
4825
4826 LogTrace("ArmCancel")
4827 SecCom_ArmCancel(self._PartitionNumber, InterfaceID)
4828end
4829
4830function SecurityPartition:PrxExecuteEmergency(tParams)
4831 local EmergencyType = tParams["EmergencyType"]
4832 local InterfaceID = tParams["InterfaceID"] or ""
4833
4834 LogTrace("ExecuteEmergency")
4835 SecCom_SendExecuteEmergency(self._PartitionNumber, EmergencyType, InterfaceID)
4836end
4837
4838function SecurityPartition:PrxExecuteFunction(tParams)
4839 local FunctionName = tParams["Function"]
4840 local InterfaceID = tParams["InterfaceID"]
4841 local trimmedCommand = string.gsub(FunctionName, " ", "")
4842
4843 LogTrace("Execute Function (%s) from InterfaceID: %s", tostring(FunctionName), tostring(InterfaceID))
4844 if (SecurityPartitionFunctions[FunctionName] ~= nil and type(SecurityPartitionFunctions[FunctionName]) == "function") then
4845 SecurityPartitionFunctions[FunctionName](self._PartitionNumber, InterfaceID)
4846 elseif (SecurityPartitionFunctions[trimmedCommand] ~= nil and type(SecurityPartitionFunctions[trimmedCommand]) == "function") then
4847 SecurityPartitionFunctions[trimmedCommand](self._PartitionNumber, InterfaceID)
4848 else
4849 LogInfo("ID specified is null or not a function name[%s]", tostring(FunctionName))
4850 end
4851end
4852
4853function SecurityPartition:PrxKeyPress(tParams)
4854 local KeyName = tParams["KeyName"]
4855 local InterfaceID = tParams["InterfaceID"] or ""
4856
4857 LogTrace("PrxKeyPress")
4858 SecCom_SendKeyPress(self._PartitionNumber, KeyName, InterfaceID)
4859end
4860
4861function SecurityPartition:PrxAdditionalInfo(tParams)
4862 local NewInfo = tParams["NewInfo"]
4863 local InfoString = tParams["InfoString"]
4864 local InterfaceID = tParams["InterfaceID"] or ""
4865 local FunctionName = tParams["FunctionName"]
4866
4867 SecCom_ProcessAdditionalInfo(self._PartitionNumber, InfoString, NewInfo, FunctionName, InterfaceID)
4868end
4869
4870function SecurityPartition:PrxSetDefaultUserCode(tParams)
4871 local NewUserCode = tParams["Code"]
4872
4873 LogTrace("PrxSetDefaultUserCode Code is >>%s<<", NewUserCode)
4874 self:SetDefaultUserCode(NewUserCode)
4875end
4876
4877function SecurityPartition:PrxSendConfirmation(tParams)
4878 local InterfaceID = tParams["InterfaceID"] or ""
4879 SecCom_SendConfirmation(self._PartitionNumber, InterfaceID)
4880end
4881
4882--[[=============================================================================
4883 Functions that are wrappered and meant to be exposed to the driver
4884===============================================================================]]
4885function SecurityPartition:RequestAdditionalInfo(Prompt, InfoStr, FunctionName, MaskData, InterfaceID)
4886 NOTIFY.REQUEST_ADDITIONAL_INFO(Prompt, InfoStr, FunctionName, MaskData, InterfaceID, self._BindingID)
4887end
4888
4889function SecurityPartition:ArmFailed(Action, InterfaceID)
4890 NOTIFY.ARM_FAILED(Action, InterfaceID, self._BindingID)
4891end
4892
4893function SecurityPartition:DisarmFailed(InterfaceID)
4894 NOTIFY.DISARM_FAILED(InterfaceID, self._BindingID)
4895end
4896
4897function SecurityPartition:GetZoneIDs()
4898 local i = 1
4899 local s = {}
4900
4901 for k, _ in pairs(self._MyZoneList) do
4902 s[i] = k
4903 i = i + 1
4904 end
4905
4906 return s
4907end
4908
4909function SecurityPartition:GetZoneCount()
4910 local i = 0
4911
4912 for _, _ in pairs(self._MyZoneList) do
4913 i = i + 1
4914 end
4915
4916 return i
4917end
4918
4919function SecurityPartition:ContainsZone(ZoneID)
4920 for _, v in pairs(self._MyZoneList) do
4921 if (v._ZoneID == ZoneID) then
4922 return true
4923 end
4924 end
4925
4926 return false
4927end
4928
4929function SecurityPartition:AddZone(ZoneID)
4930 local nZoneID = tonumber(ZoneID)
4931 local TargZone = ZoneInfoList[nZoneID]
4932
4933 if (TargZone == nil) then
4934 ZoneInformation:new(nZoneID)
4935 TargZone = ZoneInfoList[nZoneID]
4936
4937 -- force a notification to the panel driver
4938 TargZone:ZoneInfoChanged()
4939 end
4940
4941 self._MyZoneList[ZoneID] = TargZone
4942 NOTIFY.HAS_ZONE(ZoneID, self._BindingID)
4943end
4944
4945function SecurityPartition:RemoveZone(ZoneID)
4946 local TargZone = ZoneInfoList[tonumber(ZoneID)]
4947
4948 if (TargZone ~= nil) then
4949 self._MyZoneList[ZoneID] = nil
4950 NOTIFY.REMOVE_ZONE(ZoneID, self._BindingID)
4951 end
4952end
4953
4954function SecurityPartition:SetEnabled(Enabled)
4955 LogTrace("Setting Enabled flag for partition %d to %s", self._PartitionNumber, tostring(Enabled))
4956
4957 self._IsEnabled = Enabled
4958 if (not Enabled) then
4959 self._CurrentPartitionState = "Unknown"
4960 end
4961
4962 NOTIFY.PARTITION_ENABLED(self._IsEnabled, self._BindingID)
4963end
4964
4965function SecurityPartition:IsEnabled()
4966 return self._IsEnabled
4967end
4968
4969function SecurityPartition:SetPartitionState(NewState, NewStateType, TotalDelayTime, RemainingDelayTime, CodeRequiredToClear)
4970 local previousState = self._CurrentPartitionState
4971 local previousStateType = self._CurrentStateType
4972 local SendChange = ((previousState ~= NewState) or (previousStateType ~= NewStateType))
4973 local TotDelayTime = TotalDelayTime or 0
4974 local RemDelayTime = RemainingDelayTime or 0
4975
4976 LogTrace("Partition %d changed state to %s : %s From %s", tonumber(self._PartitionNumber), tostring(NewState), tostring(NewStateType), tostring(self._CurrentPartitionState))
4977 self._CurrentPartitionState = NewState
4978 self._CurrentStateType = NewStateType
4979
4980 if (self._CurrentPartitionState == AS_ALARM) then
4981 self._InAlarm = true
4982 self._CodeRequiredToClear = CodeRequiredToClear or false
4983 else
4984 self._InAlarm = false
4985 self._CodeRequiredToClear = true
4986 end
4987
4988 if ((NewState == AS_EXIT_DELAY) or (NewState == AS_ENTRY_DELAY) or (previousState == AS_EXIT_DELAY) or (previousState == AS_ENTRY_DELAY)) then
4989 local DelayType = ((NewState == AS_EXIT_DELAY) or (previousState == AS_EXIT_DELAY)) and "Exit" or "Entry"
4990 local DelayActive = ((NewState == AS_EXIT_DELAY) or (NewState == AS_ENTRY_DELAY))
4991
4992 self:EntryExitDelay(DelayType, DelayActive, TotDelayTime, RemDelayTime)
4993 SendChange = true
4994 end
4995
4996 if (SendChange) then
4997 self:NotifyPartitionState()
4998 end
4999end
5000
5001function SecurityPartition:HaveEmergency(EmergencyType)
5002 NOTIFY.EMERGENCY_TRIGGERED(EmergencyType, self._BindingID)
5003end
5004
5005function SecurityPartition:DisplayText(Message)
5006 NOTIFY.DISPLAY_TEXT(Message, self._BindingID)
5007end
5008
5009function SecurityPartition:SetCodeRequiredToArm(CodeRequired)
5010 self._CodeRequiredToArm = CodeRequired
5011 NOTIFY.CODE_REQUIRED(self._CodeRequiredToArm, self._BindingID)
5012end
5013
5014function SecurityPartition:IsCodeRequiredToArm()
5015 return self._CodeRequiredToArm
5016end
5017
5018function SecurityPartition:GetPartitionState()
5019 return self._CurrentPartitionState
5020end
5021
5022function SecurityPartition:GetPartitionStateType()
5023 return self._CurrentStateType
5024end
5025
5026function SecurityPartition:IsArmed()
5027 return ((self._CurrentPartitionState == AS_ARMED) or
5028 (self._CurrentPartitionState == AS_ENTRY_DELAY) or
5029 (self._CurrentPartitionState == AS_ALARM))
5030end
5031
5032function SecurityPartition:IsInDelay()
5033 return ((self._CurrentPartitionState == AS_EXIT_DELAY) or
5034 (self._CurrentPartitionState == AS_ENTRY_DELAY))
5035end
5036
5037function SecurityPartition:SetDefaultUserCode(NewCode)
5038 if (NewCode ~= nil) then
5039 LogTrace("Setting Default User Code for partition %d to >>%s<<", self._PartitionNumber, NewCode)
5040 self._DefaultUserCode = NewCode
5041 end
5042end
5043
5044function SecurityPartition:GetDefaultUserCode()
5045 return self._DefaultUserCode
5046end end)
5047package.preload['partition_proxy.securitypartition_commands'] = (function (...)
5048--[[=============================================================================
5049 Commands for the SecurityPartition Proxy
5050
5051 Copyright 2015 Control4 Corporation. All Rights Reserved.
5052===============================================================================]]
5053require "lib.c4_log"
5054
5055TEMPLATE_VERSION.securitypartition = "9"
5056
5057function PRX_CMD.KEY_PRESS(idBinding, tParams)
5058 LogTrace("PRX_CMD.KEY_PRESS")
5059 if (SecurityPartitionBindingList[idBinding]) then
5060 SecurityPartitionBindingList[idBinding]:PrxKeyPress(tParams)
5061 end
5062end
5063
5064function PRX_CMD.PARTITION_ARM(idBinding, tParams)
5065 LogTrace("PRX_CMD.PARTITION_ARM")
5066 if (SecurityPartitionBindingList[idBinding]) then
5067 SecurityPartitionBindingList[idBinding]:PrxPartitionArm(tParams)
5068 end
5069end
5070
5071function PRX_CMD.PARTITION_DISARM(idBinding, tParams)
5072 LogTrace("PRX_CMD.PARTITION_DISARM")
5073 if (SecurityPartitionBindingList[idBinding]) then
5074 SecurityPartitionBindingList[idBinding]:PrxPartitionDisarm(tParams)
5075 end
5076end
5077
5078function PRX_CMD.ARM_CANCEL(idBinding, tParams)
5079 LogTrace("PRX_CMD.ARM_CANCEL")
5080 if (SecurityPartitionBindingList[idBinding]) then
5081 SecurityPartitionBindingList[idBinding]:PrxArmCancel(tParams)
5082 end
5083end
5084
5085function PRX_CMD.EXECUTE_EMERGENCY(idBinding, tParams)
5086 LogTrace("PRX_CMD.EXECUTE_EMERGENCY")
5087 if (SecurityPartitionBindingList[idBinding]) then
5088 SecurityPartitionBindingList[idBinding]:PrxExecuteEmergency(tParams)
5089 end
5090end
5091
5092function PRX_CMD.EXECUTE_FUNCTION(idBinding, tParams)
5093 LogTrace("PRX_CMD.EXECUTE_FUNCTION")
5094 if (SecurityPartitionBindingList[idBinding]) then
5095 SecurityPartitionBindingList[idBinding]:PrxExecuteFunction(tParams)
5096 end
5097end
5098
5099function PRX_CMD.ADDITIONAL_INFO(idBinding, tParams)
5100 LogTrace("PRX_CMD.ADDITIONAL_INFO")
5101 if (SecurityPartitionBindingList[idBinding]) then
5102 SecurityPartitionBindingList[idBinding]:PrxAdditionalInfo(tParams)
5103 end
5104end
5105
5106function PRX_CMD.SET_DEFAULT_USER_CODE(idBinding, tParams)
5107 LogTrace("PRX_CMD.SET_DEFAULT_USER_CODE")
5108 if (SecurityPartitionBindingList[idBinding]) then
5109 SecurityPartitionBindingList[idBinding]:PrxSetDefaultUserCode(tParams)
5110 end
5111end
5112
5113function PRX_CMD.SEND_CONFIRMATION(idBinding, tParams)
5114 LogTrace("PRX_CMD.SEND_CONFIRMATION")
5115 if (SecurityPartitionBindingList[idBinding]) then
5116 SecurityPartitionBindingList[idBinding]:PrxSendConfirmation(tParams)
5117 end
5118end
5119
5120 end)
5121package.preload['partition_proxy.securitypartition_functions'] = (function (...)
5122--[[=============================================================================
5123 Functions dealing with the management of partitions and their states
5124
5125 Copyright 2015 Control4 Corporation. All Rights Reserved.
5126===============================================================================]]
5127require "common.c4_utils"
5128
5129TEMPLATE_VERSION.securitypartition = "9"
5130
5131--[[=============================================================================
5132 ArmPartitionFailed(PartitionID, Action, InterfaceID)
5133
5134 Description:
5135 Notifies the system that an arm partition has failed, and tells the UI what
5136 action if any needs to be taken in order to proceed.
5137
5138 Parameters:
5139 PartitionID(int) - The index of the partition we are arming
5140 Action(string) - Indicates the action that the UI should take to help
5141 rectify. Following is a list of actions that can be
5142 taken keypad(if a keycode is needed), bypass, or
5143 NA(general failure)
5144 InterfaceID(string) - The unique identifier string of the UI which requested
5145 the arming action. We only want that UI to respond to
5146 the failed message.
5147
5148 Returns:
5149 None
5150===============================================================================]]
5151function ArmPartitionFailed(PartitionID, Action, InterfaceID)
5152 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5153 SecurityPartitionIndexList[tonumber(PartitionID)]:ArmFailed(Action, InterfaceID)
5154 end
5155end
5156
5157--[[=============================================================================
5158 DisarmPartitionFailed(PartitionID, InterfaceID)
5159
5160 Description:
5161 Notifies the system that a disarm partition has failed.
5162
5163 Parameters:
5164 PartitionID(int) - The index of the partition we are disarming
5165 InterfaceID(string) - The unique identifier string of the UI which requested
5166 the disarming action.
5167 We only want that UI to respond to the failed message.
5168
5169 Returns:
5170 None
5171===============================================================================]]
5172function DisarmPartitionFailed(PartitionID, InterfaceID)
5173 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5174 SecurityPartitionIndexList[tonumber(PartitionID)]:DisarmFailed(InterfaceID)
5175 end
5176end
5177
5178--[[=============================================================================
5179 GetPartitionZoneIDs(PartitionID)
5180
5181 Description:
5182 Get the list of zone IDs that are associated with the specified partition
5183
5184 Parameters:
5185 PartitionID(int) - The index of the partition we are getting the list from
5186
5187 Returns:
5188 A table containing a list of the zone numbers for the specified partition
5189===============================================================================]]
5190function GetPartitionZoneIDs(PartitionID)
5191 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5192 return SecurityPartitionIndexList[tonumber(PartitionID)]:GetZoneIDs()
5193 else
5194 return nil
5195 end
5196end
5197
5198--[[=============================================================================
5199 GetPartitionZoneCount(PartitionID)
5200
5201 Description:
5202 Get the count of the zones that are associated with the zone
5203
5204 Parameters:
5205 PartitionID(int) - The index of the partition we are getting the count from
5206
5207 Returns:
5208 The zone count for the associated partition
5209===============================================================================]]
5210function GetPartitionZoneCount(PartitionID)
5211 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5212 return SecurityPartitionIndexList[tonumber(PartitionID)]:GetZoneCount()
5213 else
5214 return 0
5215 end
5216end
5217
5218--[[=============================================================================
5219 AddZoneToPartition(PartitionID, ZoneID)
5220
5221 Description:
5222 Adds the given zone to the specified partition
5223 Note: SetZoneInfo must be called before this function in order for the call
5224 to succeed
5225
5226 Parameters:
5227 PartitionID(int) - The index of the partition we are adding the zone to
5228 ZoneID(int) - The zone id that is being added to the partition
5229
5230 Returns:
5231 None
5232===============================================================================]]
5233function AddZoneToPartition(PartitionID, ZoneID)
5234 local TargPartition = SecurityPartitionIndexList[tonumber(PartitionID)]
5235
5236 if(TargPartition ~= nil) then
5237 if(not TargPartition:ContainsZone(ZoneID)) then
5238 TargPartition:AddZone(tonumber(ZoneID))
5239 ZoneInfoList[tonumber(ZoneID)]:AddToPartition(tonumber(PartitionID))
5240 end
5241 end
5242end
5243
5244--[[=============================================================================
5245 RemoveZoneFromPartition(PartitionID, ZoneID)
5246
5247 Description:
5248 Removes the given zone from the specified partition
5249
5250 Parameters:
5251 PartitionID(int) - The index of the partition we are adding the zone to
5252 ZoneID(int) - The zone id that is being added to the partition
5253
5254 Returns:
5255 None
5256===============================================================================]]
5257function RemoveZoneFromPartition(PartitionID, ZoneID)
5258 local TargPartition = SecurityPartitionIndexList[tonumber(PartitionID)]
5259
5260 if(TargPartition ~= nil) then
5261 if(TargPartition:ContainsZone(ZoneID)) then
5262 TargPartition:RemoveZone(tonumber(ZoneID))
5263 ZoneInfoList[tonumber(ZoneID)]:RemoveFromPartition(tonumber(PartitionID))
5264 end
5265 end
5266end
5267
5268--[[=============================================================================
5269 PartitionContainsZone(PartitionID, ZoneID)
5270
5271 Description:
5272 Tells if the given zone is a member of the given partition
5273
5274 Parameters:
5275 PartitionID(int) - The index of the partition we are checking
5276 ZoneID(int) - The id of the zone we are checking
5277
5278 Returns:
5279 True if the partition contains the zone; False if it doesn't
5280===============================================================================]]
5281function PartitionContainsZone(PartitionID, ZoneID)
5282 local TargPartition = SecurityPartitionIndexList[tonumber(PartitionID)]
5283
5284 if(TargPartition ~= nil) then
5285 return TargPartition:ContainsZone(ZoneID)
5286 else
5287 return false
5288 end
5289end
5290
5291--[[=============================================================================
5292 SetPartitionEnabled(PartitionID, Enabled)
5293
5294 Description:
5295 Marks the specified partition as enabled within the system. If set to false
5296 the partition will not be visible to the UI.
5297
5298 Parameters:
5299 PartitionID(int) - The index of the partition we are enabling or disabling
5300 Enabled(bool) - The state of the partition
5301
5302 Returns:
5303 None
5304===============================================================================]]
5305function SetPartitionEnabled(PartitionID, Enabled)
5306 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5307 SecurityPartitionIndexList[tonumber(PartitionID)]:SetEnabled(Enabled)
5308 end
5309end
5310
5311--[[=============================================================================
5312 SetPartitionInitializingFlag(PartitionID, InitializingFlag)
5313
5314 Description:
5315 Marks the specified partition as currently being initialized. Some behaviours
5316 are different if a setting is happening for the first time
5317
5318 Parameters:
5319 PartitionID(int) - The index of the partition we are enabling or disabling
5320 InitializingFlag(bool) - True if the partition is currently being initialized
5321
5322 Returns:
5323 None
5324===============================================================================]]
5325function SetPartitionInitializingFlag(PartitionID, InitializingFlag)
5326 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5327 SecurityPartitionIndexList[tonumber(PartitionID)]:SetInitializingFlag(InitializingFlag)
5328 end
5329end
5330
5331--[[=============================================================================
5332 IsPartitionEnabled(PartitionID)
5333
5334 Description:
5335 Identifies whether or not the specified partition is enabled
5336
5337 Parameters:
5338 PartitionID(int) - The index of the partition we are checking
5339
5340 Returns:
5341 True if the partition is enabled
5342===============================================================================]]
5343function IsPartitionEnabled(PartitionID)
5344 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5345 return SecurityPartitionIndexList[tonumber(PartitionID)]:IsEnabled()
5346 else
5347 return false
5348 end
5349end
5350
5351--[[=============================================================================
5352 SetPartitionState(PartitionID, State, StateType, TotalDelayTime, RemainingDelayTime, CodeRequiredToClear)
5353
5354 Description:
5355 Sets the specified partitions current state in the system
5356
5357 Parameters:
5358 PartitionID(int) - The number for the partition whose state is being set
5359 State(string) - The state of the partition indicated by PartitionID
5360 Following are a list of valid states(ARMED, ALARM,
5361 DISARMED_NOT_READY, DISARMED_READY, EXIT_DELAY,
5362 and ENTRY_DELAY)
5363 StateType(string) - Some description to further clarify the partition
5364 state. If the state is ARMED, the state type might
5365 be "Home" or "Away". If the state is ALARM, the
5366 state type might be "FIRE" or "BURGLARY". This
5367 may also be an empty string for other states.
5368 TotalDelayTime(int) - An optional parameter that is to be used when
5369 the state being specified is either (ENTRY_DELAY
5370 or EXIT_DELAY)
5371 RemainingDelayTime(int) - An optional parameter that is to be used when
5372 the state being specified is either (ENTRY_DELAY
5373 or EXIT_DELAY)
5374 CodeRequiredToClear(boolean) - An optional parameter that is defaulted to true.
5375 Identifies whether or not a code is required to
5376 clear the alarm event
5377
5378 Returns:
5379 None
5380===============================================================================]]
5381function SetPartitionState(PartitionID, State, StateType, TotalDelayTime, RemainingDelayTime, CodeRequiredToClear)
5382 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5383 ArmType = ArmType or ""
5384 TotalDelayTime = tonumber(TotalDelayTime) or 0
5385 RemainingDelayTime = tonumber(RemainingDelayTime) or 0
5386
5387 if (CodeRequiredToClear == nil) then
5388 CodeRequiredToClear = true
5389 end
5390
5391 SecurityPartitionIndexList[tonumber(PartitionID)]:SetPartitionState(State, StateType, TotalDelayTime, RemainingDelayTime, CodeRequiredToClear)
5392 end
5393end
5394
5395--[[=============================================================================
5396 GetPartitionState(PartitionID)
5397
5398 Description:
5399 Get the state of the partition that was specified by the given PartitionID
5400
5401 Parameters:
5402 PartitionID(int) - The index of the partition we are getting the state from
5403
5404 Returns:
5405 The state of the partition specified by the PartitionID
5406 Following are a list of states that should be returned (ARMED, ALARM,
5407 DISARMED_NOT_READY, DISARMED_READY, EXIT_DELAY, and ENTRY_DELAY)
5408===============================================================================]]
5409function GetPartitionState(PartitionID)
5410 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5411 return SecurityPartitionIndexList[tonumber(PartitionID)]:GetPartitionState()
5412 else
5413 return AS_DISARMED_READY
5414 end
5415end
5416
5417--[[=============================================================================
5418 GetPartitionStateType(PartitionID)
5419
5420 Description:
5421 Get the state type of the partition that was specified by the given PartitionID
5422
5423 Parameters:
5424 PartitionID(int) - The index of the partition we are getting the state type from
5425
5426 Returns:
5427 The description of the state for the partition specified by the PartitionID
5428===============================================================================]]
5429function GetPartitionStateType(PartitionID)
5430 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5431 return SecurityPartitionIndexList[tonumber(PartitionID)]:GetPartitionStateType()
5432 else
5433 return "UNKNOWN"
5434 end
5435end
5436
5437--[[=============================================================================
5438 SetCodeRequiredToArm(PartitionID, CodeRequired)
5439
5440 Description:
5441 Tells the system that the given partition requires a code to arm.
5442
5443 Parameters:
5444 PartitionID(int) - The index of the partition we are specifiying the status
5445 CodeRequired(bool) - True if a code is required to arm the partition, and
5446 false otherwise.
5447
5448 Returns:
5449 None
5450===============================================================================]]
5451function SetCodeRequiredToArm(PartitionID, CodeRequired)
5452 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5453 SecurityPartitionIndexList[tonumber(PartitionID)]:SetCodeRequiredToArm(CodeRequired)
5454 end
5455end
5456
5457--[[=============================================================================
5458 IsCodeRequiredToArm(PartitionID)
5459
5460 Description:
5461 Reports if currently a code is required to am the specified partition
5462
5463 Parameters:
5464 PartitionID(int) - The index of the partition we are asking about
5465
5466 Returns:
5467 True if a code is required to arm the partition, and false otherwise.
5468===============================================================================]]
5469function IsCodeRequiredToArm(PartitionID)
5470 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5471 return SecurityPartitionIndexList[tonumber(PartitionID)]:IsCodeRequiredToArm()
5472 else
5473 return false
5474 end
5475end
5476
5477--[[=============================================================================
5478 IsPartitionArmed(PartitionID)
5479
5480 Description:
5481 Returns the armed state of the partition indicated by PartitionID
5482
5483 Parameters:
5484 PartitionID(int) - The index of the partition we are getting the armed
5485 status for
5486
5487 Returns:
5488 The armed state of the partition specified
5489===============================================================================]]
5490function IsPartitionArmed(PartitionID)
5491 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5492 return SecurityPartitionIndexList[tonumber(PartitionID)]:IsArmed()
5493 else
5494 return false
5495 end
5496end
5497
5498--[[=============================================================================
5499 IsPartitionInDelay(PartitionID)
5500
5501 Description:
5502 Returns the delay information for the partition indicated by PartitionID
5503
5504 Parameters:
5505 PartitionID(int) - The index of the partition we are getting delay
5506 information for
5507
5508 Returns:
5509 True if the Partition is currently in a delay state, false otherwise
5510===============================================================================]]
5511function IsPartitionInDelay(PartitionID)
5512 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5513 return SecurityPartitionIndexList[tonumber(PartitionID)]:IsInDelay()
5514 else
5515 return false
5516 end
5517end
5518
5519--[[=============================================================================
5520 DisplayPartitionText(PartitionID, Message)
5521
5522 Description:
5523 Writes the given message to the specified partition
5524
5525 Parameters:
5526 PartitionID(int) - The index of the partition we are writing the message to
5527 Message(string) - The message to be written to the UI
5528
5529 Returns:
5530 Writes the given message to the display field of the UI
5531===============================================================================]]
5532function DisplayPartitionText(PartitionID, Message)
5533 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5534 SecurityPartitionIndexList[tonumber(PartitionID)]:DisplayText(Message)
5535 end
5536end
5537
5538--[[=============================================================================
5539 HaveEmergency(EmergencyName)
5540
5541 Description:
5542 Notifies all partitions that an emergency has been triggered.
5543
5544 Parameters:
5545 EmergencyName(string) - The type of emergency that is being triggered.
5546 Current Emergency Types:
5547 Fire, Medical, Police, and Panic.
5548 However other strings could be sent if desired. The
5549 UI just may not have icons for them
5550
5551 Returns:
5552 None
5553===============================================================================]]
5554function HaveEmergency(EmergencyName)
5555 for _, v in pairs(SecurityPartitionIndexList) do
5556 v:HaveEmergency(EmergencyName)
5557 end
5558end
5559
5560--[[=============================================================================
5561 PartitionRequestAdditionalInfo(PartitionID, Prompt, ParmList, FunctionName, MaskData, InterfaceID)
5562
5563 Description:
5564 Provides a mechanism to ask the UI for the given partition to provide more
5565 info, the most common use case would be if a user code or an installers code
5566 would be required to complete a desired action.
5567
5568 Parameters:
5569 PartitionID(int) - The index of the partition to provide the new information.
5570 Prompt(string) - The prompt that the UI will display when asking for the
5571 additional information.
5572 ParmList(string) - A string of current information that will be passed
5573 along as the new information is requested and then passed
5574 back to this driver. This string would contain information
5575 as to what the driver should do with the new information.
5576 Usually it would indicate which routine should be called
5577 and what parameters should be passed to that routine.
5578 FunctionName(string) - The name of the function to be called on return.
5579 MaskData(boolean) - True the input off the keypad will be obscured when
5580 entering data, False the input will be visiable
5581 InterfaceId(string) - A unique string identifying which interface this
5582 request is being sent to. Usually this would be the
5583 InterfaceId paramater given to the command that is
5584 requesting more info. An empty string is also legal
5585 the command will be acted upon by all navigator
5586 interfaces.
5587===============================================================================]]
5588function PartitionRequestAdditionalInfo(PartitionID, Prompt, ParmList, FunctionName, MaskData, InterfaceId)
5589 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5590 SecurityPartitionIndexList[tonumber(PartitionID)]:RequestAdditionalInfo(tostring(Prompt), ParmList, tostring(FunctionName), toboolean(MaskData), tostring(InterfaceId))
5591 end
5592end
5593
5594--[[=============================================================================
5595 SetDefaultUserCode(PartitionID, NewCode)
5596
5597 Description:
5598 Provides a mechanism to store a default user code for times when the user
5599 will be unavailable(i.e. Composer programming events)
5600
5601 Parameters:
5602 PartitionID(int) - The index of the partition to provide the new information.
5603 NewCode(string) - The default user code that will be provided when there is
5604 no user to provide the input.
5605===============================================================================]]
5606function SetDefaultUserCode(PartitionID, NewCode)
5607 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5608 SecurityPartitionIndexList[tonumber(PartitionID)]:SetDefaultUserCode(NewCode)
5609 end
5610end
5611
5612--[[=============================================================================
5613 SetDefaultUserCode(PartitionID, NewCode)
5614
5615 Description:
5616 Returns the stored default user code
5617
5618 Parameters:
5619 PartitionID(int) - The index of the partition to provide the new information.
5620===============================================================================]]
5621function GetDefaultUserCode(PartitionID)
5622 if (SecurityPartitionIndexList[tonumber(PartitionID)] ~= nil) then
5623 return SecurityPartitionIndexList[tonumber(PartitionID)]:GetDefaultUserCode()
5624 else
5625 return ""
5626 end
5627end
5628 end)
5629package.preload['partition_proxy.securitypartition_notifies'] = (function (...)
5630--[[=============================================================================
5631 Notifications for the SecurityPartition Proxy
5632
5633 Copyright 2015 Control4 Corporation. All Rights Reserved.
5634===============================================================================]]
5635require "common.c4_notify"
5636
5637TEMPLATE_VERSION.securitypartition = "9"
5638
5639function NOTIFY.DISPLAY_TEXT(DispText, BindingID)
5640 local DisplayTextParams = {}
5641
5642 LogTrace("Sending Display Text on Binding %s: %s", tostring(BindingID), tostring(DispText))
5643 C4:SendToProxy(BindingID, "DISPLAY_TEXT", DispText)
5644end
5645
5646function NOTIFY.PARTITION_STATE(NewPartitionState, StateType, DelayTotal, DelayRemaining, CodeRequiredToClear, BindingID)
5647 local StateParams = {}
5648
5649 LogTrace("NOTIFY.PARTITION_STATE %s >>%s<< %d %d %d", tostring(NewPartitionState), tostring(StateType), tonumber(DelayTotal), tonumber(DelayRemaining), tonumber(BindingID))
5650 StateParams["STATE"] = NewPartitionState
5651 StateParams["TYPE"] = StateType
5652 StateParams["DELAY_TIME_TOTAL"] = DelayTotal
5653 StateParams["DELAY_TIME_REMAINING"] = DelayRemaining
5654 StateParams["CODE_REQUIRED_TO_CLEAR"] = CodeRequiredToClear
5655
5656 SendNotify("PARTITION_STATE", StateParams, BindingID)
5657end
5658
5659function NOTIFY.PARTITION_STATE_INIT(NewPartitionState, StateType, DelayTotal, DelayRemaining, CodeRequiredToClear, BindingID)
5660 local StateParams = {}
5661
5662 LogTrace("NOTIFY.PARTITION_STATE_INIT: %s %s %d %d %d", tostring(NewPartitionState), tostring(StateType), tonumber(DelayTotal), tonumber(DelayRemaining), tonumber(BindingID))
5663 StateParams["STATE"] = NewPartitionState
5664 StateParams["TYPE"] = StateType
5665 StateParams["DELAY_TIME_TOTAL"] = DelayTotal
5666 StateParams["DELAY_TIME_REMAINING"] = DelayRemaining
5667 StateParams["CODE_REQUIRED_TO_CLEAR"] = CodeRequiredToClear
5668
5669 SendNotify("PARTITION_STATE_INIT", StateParams, BindingID)
5670end
5671
5672function NOTIFY.PARTITION_ENABLED(IsEnabled, BindingID)
5673 local EnabledParams = {}
5674
5675 LogTrace("NOTIFY.PARTITION_ENABLED: %s %d", tostring(IsEnabled), tonumber(BindingID))
5676 EnabledParams["ENABLED"] = tostring(IsEnabled)
5677
5678 SendNotify("PARTITION_ENABLED", EnabledParams, BindingID)
5679end
5680
5681function NOTIFY.ZONE_STATE(ZoneID, IsOpen, IsBypassed, BindingID)
5682 local StateParams = {}
5683
5684 LogTrace("NOTIFY.ZONE_STATE: %d %s %s %d", tonumber(ZoneID), tostring(IsOpen), tostring(IsBypassed), tonumber(BindingID))
5685 StateParams["ZONE_ID"] = tostring(ZoneID)
5686 StateParams["ZONE_OPEN"] = tostring(IsOpen)
5687 StateParams["ZONE_BYPASSED"] = tostring(IsBypassed)
5688
5689 SendNotify("ZONE_STATE", StateParams, BindingID)
5690end
5691
5692function NOTIFY.EMERGENCY_TRIGGERED(EmergencyType, BindingID)
5693 local TriggerParams = {}
5694
5695 LogTrace("Sending EMERGENCY_TRIGGERED: >>%s<< on binding: %d", tostring(EmergencyType), tonumber(BindingID))
5696 TriggerParams["TYPE"] = EmergencyType
5697
5698 SendNotify("EMERGENCY_TRIGGERED", TriggerParams, BindingID)
5699end
5700
5701function NOTIFY.DISARM_FAILED(InterfaceID, BindingID)
5702 local DisarmFailedParams = {}
5703 DisarmFailedParams["INTERFACE_ID"] = InterfaceID
5704
5705 LogTrace("NOTIFY.DISARM_FAILED: %d", tonumber(BindingID))
5706
5707 SendNotify("DISARM_FAILED", DisarmFailedParams, BindingID)
5708end
5709
5710function NOTIFY.ARM_FAILED(Action, InterfaceID, BindingID)
5711 local ArmFailedParams = {}
5712 ArmFailedParams["ACTION"] = Action
5713 ArmFailedParams["INTERFACE_ID"] = InterfaceID
5714
5715 LogTrace("NOTIFY.ARM_FAILED: %s %d", tostring(Action), tonumber(BindingID))
5716 SendNotify("ARM_FAILED", ArmFailedParams, BindingID)
5717end
5718
5719function NOTIFY.HAS_ZONE(ZoneID, BindingID)
5720 local ZoneParams = {}
5721
5722 LogTrace("NOTIFY.HAS_ZONE: %d %d", tonumber(ZoneID), tonumber(BindingID))
5723 ZoneParams["ZONE_ID"] = tostring(ZoneID)
5724
5725 SendNotify("HAS_ZONE", ZoneParams, BindingID)
5726end
5727
5728function NOTIFY.REMOVE_ZONE(ZoneID, BindingID)
5729 local ZoneParams = {}
5730
5731 LogTrace("NOTIFY.REMOVE_ZONE: %d %d", tonumber(ZoneID), tonumber(BindingID))
5732 ZoneParams["ZONE_ID"] = tostring(ZoneID)
5733
5734 SendNotify("REMOVE_ZONE", ZoneParams, BindingID)
5735end
5736
5737function NOTIFY.CLEAR_ZONE_LIST(BindingID)
5738 LogTrace("NOTIFY.CLEAR_ZONE_LIST: %d", tonumber(BindingID))
5739
5740 SendNotify("CLEAR_ZONE_LIST", "", BindingID)
5741end
5742
5743function NOTIFY.CODE_REQUIRED(CodeRequiredToArm, BindingID)
5744 local NotifyParams = {}
5745
5746 LogTrace("Sending CODE_REQUIRED: >>%s<< on binding: %d", tostring(CodeRequiredToArm), tonumber(BindingID))
5747 NotifyParams["CODE_REQUIRED_TO_ARM"] = tostring(CodeRequiredToArm)
5748
5749 C4:SendToProxy(BindingID, "CODE_REQUIRED", NotifyParams)
5750end
5751
5752function NOTIFY.REQUEST_ADDITIONAL_INFO(Prompt, InfoString, FunctionName, MaskData, InterfaceID, BindingID)
5753 local ParmList = {}
5754
5755 LogTrace("NOTIFY.REQUEST_MORE_INFO: %s %s %d", tostring(Prompt), InfoString, tonumber(BindingID))
5756 ParmList["PROMPT"] = Prompt
5757 ParmList["INFO_STRING"] = InfoString
5758 ParmList["FUNCTION_NAME"] = FunctionName
5759 ParmList["MASK_DATA"] = tostring(MaskData)
5760 ParmList["INTERFACE_ID"] = InterfaceID
5761
5762 SendNotify("REQUEST_ADDITIONAL_INFO", ParmList, BindingID)
5763end
5764
5765function NOTIFY.PARTITION_INFO(xml, BindingID)
5766 LogTrace("NOTIFY.PARTITION_INFO: %s %d", tostring(xml), tonumber(BindingID))
5767
5768 C4:SendToProxy(BindingID, "PARTITION_INFO", xml)
5769end end)
5770package.preload['actions'] = (function (...)
5771--[[=============================================================================
5772 Lua Action Code
5773
5774 Copyright 2016 Control4 Corporation. All Rights Reserved.
5775===============================================================================]]
5776
5777-- This macro is utilized to identify the version string of the driver template version used.
5778if (TEMPLATE_VERSION ~= nil) then
5779 TEMPLATE_VERSION.actions = "2016.01.08"
5780end
5781
5782-- TODO: Create a function for each action defined in the driver
5783
5784function LUA_ACTION.TemplateVersion()
5785 TemplateVersion()
5786end
5787
5788
5789function LUA_ACTION.PrintVolumeCurve()
5790 print("===== Volume Curve =====")
5791 for j,k in pairs(tVolumeCurve) do
5792 print(j,k)
5793 end
5794end
5795
5796 end)
5797package.preload['connections'] = (function (...)
5798--[[=============================================================================
5799 Functions for managing the status of the drivers bindings and connection state
5800
5801 Copyright 2015 Control4 Corporation. All Rights Reserved.
5802===============================================================================]]
5803
5804require "common.c4_networkserver_connection"
5805require "common.c4_network_connection"
5806require "common.c4_serial_connection"
5807require "common.c4_ir_connection"
5808require "common.c4_url_connection"
5809require "qolsys_helper"
5810
5811if (TEMPLATE_VERSION ~= nil) then
5812 TEMPLATE_VERSION.connections = "2014.10.31"
5813end
5814
5815-- constants
5816COM_USE_ACK = false
5817COM_COMMAND_DELAY_MILLISECONDS = 250
5818COM_COMMAND_RESPONSE_TIMEOUT_SECONDS = 4
5819
5820NETWORK_PORT = 12345
5821IR_BINDING_ID = 2
5822SERIAL_BINDING_ID = 1
5823NETWORK_BINDING_ID = 6000
5824
5825--[[=============================================================================
5826 OnSerialConnectionChanged(idBinding, class, bIsBound)
5827
5828 Description:
5829 Function called when a serial binding changes state(bound or unbound).
5830
5831 Parameters:
5832 idBinding(int) - ID of the binding whose state has changed (SERIAL_BINDING_ID).
5833 class(string) - Class of binding that has changed.
5834 A single binding can have multiple classes(i.e. COMPONENT,
5835 STEREO, RS_232, etc).
5836 This indicates which has been bound or unbound.
5837 bIsBound(bool) - Whether the binding has been bound or unbound.
5838
5839 Returns:
5840 None
5841===============================================================================]]
5842function OnSerialConnectionChanged(idBinding, class, bIsBound)
5843
5844 if (bIsBound) then
5845 EstablishCommunication()
5846 else
5847 TearDownCommunication()
5848 end
5849end
5850
5851--[[=============================================================================
5852 OnIRConnectionChanged(idBinding, class, bIsBound)
5853
5854 Description:
5855 Function called when an IR binding changes state(bound or unbound).
5856
5857 Parameters:
5858 idBinding(int) - ID of the binding whose state has changed (SERIAL_BINDING_ID).
5859 class(string) - Class of binding that has changed.
5860 A single binding can have multiple classes(i.e. COMPONENT,
5861 STEREO, RS_232, etc).
5862 This indicates which has been bound or unbound.
5863 bIsBound(bool) - Whether the binding has been bound or unbound.
5864
5865 Returns:
5866 None
5867===============================================================================]]
5868function OnIRConnectionChanged(idBinding, class, bIsBound)
5869
5870end
5871
5872--[[=============================================================================
5873 OnNetworkConnectionChanged(idBinding, bIsBound)
5874
5875 Description:
5876 Function called when a network binding changes state(bound or unbound).
5877
5878 Parameters:
5879 idBinding(int) - ID of the binding whose state has changed.
5880 bIsBound(bool) - Whether the binding has been bound or unbound.
5881
5882 Returns:
5883 None
5884===============================================================================]]
5885function OnNetworkConnectionChanged(idBinding, bIsBound)
5886
5887end
5888
5889--[[=============================================================================
5890 OnNetworkStatusChanged(idBinding, nPort, sStatus)
5891
5892 Description:
5893 Called when the network connection status changes. Sets the updated status of the specified binding
5894
5895 Parameters:
5896 idBinding(int) - ID of the binding whose status has changed
5897 nPort(int) - The communication port of the specified bindings connection
5898 sStatus(string) - "ONLINE" if the connection status is to be set to Online,
5899 any other value will set the status to Offline
5900
5901 Returns:
5902 None
5903===============================================================================]]
5904function OnNetworkStatusChanged(idBinding, nPort, sStatus)
5905 if(sStatus == "ONLINE") then
5906 OnNetworkConnected(idBinding, nPort)
5907 elseif(sStatus == "OFFLINE") then
5908 OnNetworkDisconnected(idBinding, nPort)
5909 end
5910end
5911
5912function OnNetworkConnected(idBinding, nPort)
5913 RequestSummary()
5914end
5915
5916
5917function OnNetworkDisconnected(idBinding, nPort)
5918 SetAllPartitionsOffline()
5919 --ZoneInfoList = {}
5920end
5921
5922--[[=============================================================================
5923 OnURLConnectionChanged(url)
5924
5925 Description:
5926 Function called when the c4_url_connection is created.
5927
5928 Parameters:
5929 url - url used by the url connection.
5930
5931 Returns:
5932 None
5933===============================================================================]]
5934function OnURLConnectionChanged(url)
5935
5936end
5937
5938
5939--[[=============================================================================
5940 OnConnectionChanged(idBinding, class, bIsBound)
5941 Description:
5942 Default call when a binding changes state(bound or unbound).
5943
5944 Parameters:
5945 idBinding(int) - ID of the binding whose state has changed.
5946 class(string) - Class of binding that has changed.
5947 A single binding can have multiple classes(i.e. COMPONENT,
5948 STEREO, RS_232, etc).
5949 This indicates which has been bound or unbound.
5950 bIsBound(bool) - Whether the binding has been bound or unbound.
5951
5952 Returns:
5953 None
5954===============================================================================]]
5955function OnConnectionChanged(idBinding, class, bIsBound)
5956
5957end
5958
5959
5960--[[=============================================================================
5961 DoEvents()
5962
5963 Description:
5964
5965
5966 Parameters:
5967 None
5968
5969 Returns:
5970 None
5971===============================================================================]]
5972function DoEvents()
5973end
5974
5975--[[=============================================================================
5976 SendKeepAlivePollingCommand()
5977
5978 Description:
5979 Sends a driver specific polling command to the connected system
5980
5981 Parameters:
5982 None
5983
5984 Returns:
5985 None
5986===============================================================================]]
5987function SendKeepAlivePollingCommand()
5988 --TODO: Implement the keep alive command for the network connected system if required.
5989 local emptyPacket = 'keep-alive'
5990 SendCommand(emptyPacket)
5991end end)
5992package.preload['device_messages'] = (function (...)
5993--[[=============================================================================
5994 Functions for Getting, Handling, and Dispatching Messages
5995
5996 Copyright 2016 Control4 Corporation. All Rights Reserved.
5997===============================================================================]]
5998
5999-- This macro is utilized to identify the version string of the driver template version used.
6000if (TEMPLATE_VERSION ~= nil) then
6001 TEMPLATE_VERSION.device_messages = "2016.06.23"
6002end
6003
6004require "panel_proxy.zone_info"
6005
6006--[[=============================================================================
6007 EstablishCommunication()
6008
6009 Description:
6010 Called when the serial cable is connected to the panel.
6011 Begin any communication protocol initialization.
6012===============================================================================]]
6013function EstablishCommunication()
6014 LogTrace("Establish Communication...")
6015end
6016
6017--[[=============================================================================
6018 TearDownCommunication()
6019
6020 Description:
6021 Called when the serial cable is disconnected from the panel. Do any needed cleanup.
6022===============================================================================]]
6023function TearDownCommunication()
6024 LogTrace("Tear Down Communication...")
6025
6026 SetAllPartitionsOffline()
6027end
6028
6029--[[=============================================================================
6030 GetMessage()
6031
6032 Description:
6033 Used to retrieve a message from the communication buffer. Each driver is
6034 responsible for parsing that communication from the buffer.
6035
6036 Parameters:
6037 None
6038
6039 Returns:
6040 A single message from the communication buffer
6041===============================================================================]]
6042function GetMessage()
6043 local message = ""
6044 local endChar = '\n'
6045 if ((gReceiveBuffer ~= nil) and (gReceiveBuffer ~= "")) then
6046 local pos = string.find(gReceiveBuffer,endChar)
6047 message = string.sub(gReceiveBuffer,0,pos)
6048 gReceiveBuffer = string.sub(gReceiveBuffer,pos+1)
6049 end
6050 return message
6051end
6052
6053--[[=============================================================================
6054 HandleMessage(message)]
6055
6056 Description
6057 This is where we parse the messages returned from the GetMessage()
6058 function into a command and data. The call to 'DispatchMessage' will use the
6059 'name' variable as a key to determine which handler routine, function, should
6060 be called in the DEV_MSG table. The 'value' variable will then be passed as
6061 a string parameter to that routine.
6062
6063 Parameters
6064 message(string) - Message string containing the function and value to be
6065 sent to DispatchMessage
6066===============================================================================]]
6067function HandleMessage(message)
6068
6069 LogTrace("HandleMessage. Message is ==>%s<==", message)
6070 if(string.match(message,"ACK\n")) then
6071 return
6072 end
6073 local pattern = "(.+)\n()"
6074 local event,pos = string.match(message,pattern)
6075 local decoded = C4:JsonDecode(event)
6076 local name = decoded[EVENT_KEY]
6077 local value = decoded
6078 -- TODO: Parse messages and DispatchMessage
6079
6080
6081 DispatchMessage(name, value)
6082end
6083
6084--[[=============================================================================
6085 DispatchMessage(MsgKey, MsgData)
6086
6087 Description
6088 Parse routine that will call the routines to handle the information returned
6089 by the connected system.
6090
6091 Parameters
6092 MsgKey(string) - The function to be called from within DispatchMessage
6093 MsgData(string) - The parameters to be passed to the function found in MsgKey
6094===============================================================================]]
6095function DispatchMessage(MsgKey, MsgData)
6096
6097 if (DEV_MSG[MsgKey] ~= nil and (type(DEV_MSG[MsgKey]) == "function")) then
6098 LogInfo("DEV_MSG.%s: %s", tostring(MsgKey), tostring(MsgData))
6099 DEV_MSG[MsgKey](MsgData)
6100 else
6101 LogTrace("HandleMessage: Unhandled command = %s", tostring(MsgKey))
6102 end
6103end
6104
6105--[[=============================================================================
6106 SendCommand(CommandStr)
6107
6108 Description
6109 Parse routine that will call the routines to handle the information returned
6110 by the connected system.
6111
6112 Parameters
6113 CommandStr(string) - The command to be passed on to the security panel
6114===============================================================================]]
6115function SendCommand(CommandStr)
6116 gCon:SendCommand(CommandStr)
6117end
6118
6119------------------------------------------------------------------------------------
6120
6121--[[=============================================================================
6122 ZoneTypeConvert_3rdPartyToC4(ZoneType3rdParty)
6123
6124 Description
6125 Each 3rd party system may have its own scheme for defining zone types. This
6126 routine will map a given 3rd party zone type to one of the C4 zone types
6127 found in the 'ZoneTypes' table in the file panel_proxy.zone_info
6128
6129 Parameters
6130 ZoneType3rdParty(int) - An integer representing a zone type as defined by
6131 the 3rd party driver
6132
6133 Returns:
6134 A corresponding value for a C4 defined zone type
6135===============================================================================]]
6136function ZoneTypeConvert_3rdPartyToC4(ZoneType3rdParty)
6137 local qzone = tonumber(ZoneType3rdParty)
6138 local qzt = QolsysZoneTypes
6139 if qzone == qzt.CONTACT or qzone == qzt.CONTACT_MULTI_FUNCTION or qzone == qzt.RF_SENSOR then
6140 return ZoneTypes.CONTACT_SENSOR
6141 elseif qzone == qzt.MOTION or qzone == qzt.PANEL_MOTION then
6142 return ZoneTypes.MOTION_SENSOR
6143 elseif qzone == qzt.SMOKE_HEAT or qzone == qzt.SMOKE_MULTI_FUNCTION then
6144 return ZoneTypes.SMOKE
6145 elseif qzone == qzt.CARBON_MONOXIDE then
6146 return ZoneTypes.CARBON_MONOXIDE
6147 elseif qzone == qzt.WATER then
6148 return ZoneTypes.WATER
6149 elseif qzone == qzt.GLASSBREAK then
6150 return ZoneTypes.GLASS_BREAK
6151 end
6152
6153 return ZoneTypes.UNKNOWN
6154end
6155
6156--[[=============================================================================
6157 ZoneTypeConvert_C4To3rdParty(ZoneTypeC4)
6158
6159 Description
6160 Each 3rd party system may have its own scheme for defining zone types. This
6161 routine will map a given C4 zone type found in the 'ZoneTypes' table to a
6162 zone type expected by the 3rd party panel
6163
6164 Parameters
6165 ZoneTypeC4(int) - An integer representing a zone type as defined by C4
6166
6167 Returns:
6168 A corresponding value for a 3rd party defined zone type
6169===============================================================================]]
6170function ZoneTypeConvert_C4To3rdParty(ZoneTypeC4)
6171 local c4zone = tonumber(ZoneType3rdParty)
6172 if c4zone == ZoneTypes.UNKNOWN then
6173 return QolsysZoneTypes.UNKNOWN
6174 elseif c4zone == ZoneTypes.CONTACT_SENSOR then
6175 return QolsysZoneTypes.CONTACT
6176 elseif c4zone == ZoneTypes.EXTERIOR_DOOR then
6177 return QolsysZoneTypes.CONTACT
6178 elseif c4zone == ZoneTypes.EXTERIOR_WINDOW then
6179 return QolsysZoneTypes.CONTACT
6180 elseif c4zone == ZoneTypes.INTERIOR_DOOR then
6181 return QolsysZoneTypes.CONTACT
6182 elseif c4zone == ZoneTypes.MOTION_SENSOR then
6183 return QolsysZoneTypes.MOTION
6184 elseif c4zone == ZoneTypes.FIRE then
6185 return QolsysZoneTypes.SMOKE_HEAT
6186 elseif c4zone == ZoneTypes.GAS then
6187 return QolsysZoneTypes.UNKNOWN
6188 elseif c4zone == ZoneTypes.CARBON_MONOXIDE then
6189 return QolsysZoneTypes.CARBON_MONOXIDE
6190 elseif c4zone == ZoneTypes.HEAT then
6191 return QolsysZoneTypes.SMOKE_HEAT
6192 elseif c4zone == ZoneTypes.WATER then
6193 return QolsysZoneTypes.WATER
6194 elseif c4zone == ZoneTypes.SMOKE then
6195 return QolsysZoneTypes.SMOKE_HEAT
6196 elseif c4zone == ZoneTypes.PRESSURE then
6197 return QolsysZoneTypes.UNKNOWN
6198 elseif c4zone == ZoneTypes.GLASS_BREAK then
6199 return QolsysZoneTypes.GLASSBREAK
6200 elseif c4zone == ZoneTypes.GATE then
6201 return QolsysZoneTypes.CONTACT_SENSOR
6202 elseif c4zone == ZoneTypes.GARAGE_DOOR then
6203 return QolsysZoneTypes.CONTACT_SENSOR
6204 end
6205 return ZoneTypeC4
6206end
6207
6208
6209------------------------------------------------------------------------------------
6210-- TODO: Create "DEV_MSG.<function_name>" functions for all messages that will be
6211-- called by dispatch message
6212------------------------------------------------------------------------------------
6213function DEV_MSG.ARMING(value)
6214 local name = value[ARMING_TYPE_KEY]
6215 DispatchMessage(name, value)
6216end
6217
6218function DEV_MSG.ALARM(value)
6219 local alarmType = value[ALARM_TYPE_KEY]
6220 if alarmType == "" then
6221 alarmType = "ALARM"
6222 elseif alarmType == FIRE_VAL then
6223 HaveEmergency(alarmType)
6224 elseif alarmType == POLICE_VAL then
6225 HaveEmergency(alarmType)
6226 elseif alarmType == AUX_VAL then
6227 HaveEmergency(alarmType)
6228 end
6229 local partition_id = value[PARTITION_KEY] + 1
6230 StartTroubleCondition(alarmType)
6231 SetPartitionState(partition_id, AS_ALARM, alarmType)
6232end
6233
6234
6235function DEV_MSG.INFO(value)
6236 local name = value[INFO_TYPE_KEY]
6237 DispatchMessage(name, value)
6238end
6239
6240function DEV_MSG.ZONE_EVENT(value)
6241 local name = value[ZONE_EVENT_TYPE_KEY]
6242 DispatchMessage(name, value)
6243end
6244
6245function DEV_MSG.ERROR(value)
6246 local name = value[ERROR_TYPE_KEY]
6247 DispatchMessage(name, value)
6248end
6249
6250
6251
6252function DEV_MSG.ARM_STAY(value)
6253 --increment partition by 1 because iqpanel uses 0-3
6254 local partition_id = value[PARTITION_KEY] + 1
6255 SetPartitionState(partition_id,AS_ARMED,'Stay')
6256end
6257
6258function DEV_MSG.ARM_AWAY(value)
6259 --increment partition by 1 because iqpanel uses 0-3
6260 local partition_id = value[PARTITION_KEY] + 1
6261 SetPartitionState(partition_id,AS_ARMED,'Away')
6262end
6263
6264function DEV_MSG.DISARM(value)
6265 --increment partition by 1 because iqpanel uses 0-3
6266 local partition_id = value[PARTITION_KEY] + 1
6267 SetPartitionState(partition_id, AS_DISARMED_READY)
6268 ClearTroubleCondition('')
6269end
6270
6271--deprecated but still support
6272function DEV_MSG.ARM_AWAY_EXIT_DELAY(value)
6273 DEV_MSG.EXIT_DELAY(value)
6274end
6275
6276function DEV_MSG.EXIT_DELAY(value)
6277 local partitionID = value[PARTITION_KEY] + 1
6278 local exitDelay = value[DELAY_KEY] or value[EXIT_DELAY_KEY] or 60
6279 SetPartitionState(partitionID, AS_EXIT_DELAY, "", exitDelay, exitDelay)
6280end
6281
6282function DEV_MSG.ENTRY_DELAY(value)
6283 local partitionID = value[PARTITION_KEY] + 1
6284 local entryDelay = value[DELAY_KEY] or value[ENTRY_DELAY_KEY] or 30
6285 SetPartitionState(partitionID, AS_ENTRY_DELAY, "", entryDelay, entryDelay)
6286end
6287
6288function DEV_MSG.SUMMARY(value)
6289 --SetAllPartitionsOffline()
6290 nonce = value[NONCE_KEY]
6291 if nonce == "manual" or GetTotalZoneCount() == 0 then
6292 SetPartitionEnabled(1, false)
6293 SetPartitionEnabled(2, false)
6294 for _,z in ipairs(ZoneInfoList) do
6295 RemoveZone(z._ZoneID)
6296 end
6297 end
6298 local plist = value[PARTITION_LIST_KEY]
6299 for _,p in ipairs(plist) do
6300 --increment partition by 1 because iqpanel uses 0-3
6301 local pid = p[PARTITION_KEY] + 1
6302 SetPartitionInitializingFlag(pid, true)
6303 SetPartitionEnabled(pid, true)
6304 local pStatus = p[PARTITION_STATUS_KEY]
6305 local sec_arm = p[SECURE_ARM_KEY]
6306 SetCodeRequiredToArm(pid, sec_arm)
6307 --handle partition status
6308 if(pStatus == DISARM_VAL) then
6309 SetPartitionState(pid,AS_DISARMED_READY)
6310 DisplayPartitionText(pid, "")
6311 elseif(pStatus == ARM_AWAY_VAL) then
6312 SetPartitionState(pid,AS_ARMED,'Away')
6313 DisplayPartitionText(pid, "Away")
6314 elseif(pStatus == ARM_STAY_VAL) then
6315 SetPartitionState(pid,AS_ARMED,'Stay')
6316 DisplayPartitionText(pid, 'Stay')
6317 end
6318 --handle partition zones
6319 local zoneList = p[ZONE_LIST_KEY]
6320 for _,z in ipairs(zoneList) do
6321 local zoneID = z[ZONE_ID_KEY]
6322 local zoneStatus = z[ZONE_STATUS_KEY]
6323 local zoneName = z[ZONE_NAME_KEY]
6324 local qzoneType = z[ZONE_TYPE_KEY]
6325 local c4zoneType = ZoneTypeConvert_3rdPartyToC4(qzoneType)
6326 SetZoneInfo(zoneID, zoneName, qzoneType, c4zoneType)
6327 AddZoneToPartition(pid, zoneID)
6328 if(zoneStatus == ZONE_CLOSED_VAL or zoneStatus == ZONE_NORMAL_VAL or zoneStatus == ZONE_IDLE_VAL or zoneStatus == ZONE_ACTIVE_VAL) then
6329 SetZoneState(zoneID, false, true) --SetZoneState(ZoneID, IsOpen, Initializing)
6330 else
6331 SetZoneState(zoneID, true, true) --SetZoneState(ZoneID, IsOpen, Initializing)
6332 end
6333 end
6334 SetPartitionInitializingFlag(pid, false)
6335 end
6336end
6337
6338function DEV_MSG.SECURE_ARM(value)
6339 local pid = value[PARTITION_KEY] + 1
6340 local state = value[VALUE_KEY]
6341 SetCodeRequiredToArm(pid, state)
6342
6343end
6344
6345function DEV_MSG.ZONE_ADD(value)
6346 local z = value[ZONE_KEY]
6347 local zoneID = z[ZONE_ID_KEY]
6348 local zoneStatus = z[ZONE_STATUS_KEY]
6349 local zoneName = z[ZONE_NAME_KEY]
6350 local pid = z[PARTITION_KEY] + 1
6351 local qzoneType = z[ZONE_TYPE_KEY]
6352 local c4zoneType = ZoneTypeConvert_3rdPartyToC4(qzoneType)
6353 SetZoneInfo(zoneID, zoneName, qzoneType, c4zoneType)
6354 AddZoneToPartition(pid, zoneID)
6355 if(zoneStatus == ZONE_CLOSED_VAL or zoneStatus == ZONE_NORMAL_VAL or zoneStatus == ZONE_IDLE_VAL or zoneStatus == ZONE_ACTIVE_VAL) then
6356 SetZoneState(zoneID, false, true) --SetZoneState(ZoneID, IsOpen, Initializing)
6357 else
6358 SetZoneState(zoneID, true, true) --SetZoneState(ZoneID, IsOpen, Initializing)
6359 end
6360end
6361
6362function DEV_MSG.ZONE_ACTIVE(value)
6363 local z = value[ZONE_KEY]
6364 local zoneID = z[ZONE_ID_KEY]
6365 local zoneStatus = z[ZONE_STATUS_KEY]
6366 if(not IsZoneValid(zoneID)) then
6367 return
6368 end
6369 if(zoneStatus == ZONE_CLOSED_VAL or zoneStatus == ZONE_NORMAL_VAL or zoneStatus == ZONE_IDLE_VAL or zoneStatus == ZONE_ACTIVE_VAL) then
6370 SetZoneState(zoneID, false) --SetZoneState(ZoneID, IsOpen, Initializing)
6371 else
6372 SetZoneState(zoneID, true) --SetZoneState(ZoneID, IsOpen, Initializing)
6373 end
6374end
6375
6376function DEV_MSG.ZONE_UPDATE(value)
6377 local z = value[ZONE_KEY]
6378 local zoneID = z[ZONE_ID_KEY]
6379 local zoneStatus = z[ZONE_STATUS_KEY]
6380 local zoneName = z[ZONE_NAME_KEY]
6381 local pid = z[PARTITION_KEY]
6382 local qzoneType = z[ZONE_TYPE_KEY]
6383 local c4zoneType = ZoneTypeConvert_3rdPartyToC4(qzoneType)
6384 SetZoneInfo(zoneID, zoneName, qzoneType, c4zoneType) --todo change 1,1 to c4type, qzone type
6385 AddZoneToPartition(pid, zoneID)
6386 if(zoneStatus == ZONE_CLOSED_VAL or zoneStatus == ZONE_NORMAL_VAL or zoneStatus == ZONE_IDLE_VAL or zoneStatus == ZONE_ACTIVE_VAL) then
6387 SetZoneState(zoneID, false, true) --SetZoneState(ZoneID, IsOpen, Initializing)
6388 else
6389 SetZoneState(zoneID, true, true) --SetZoneState(ZoneID, IsOpen, Initializing)
6390 end
6391end
6392
6393function DEV_MSG.ZONE_DELETE(value)
6394 local z = value[ZONE_KEY]
6395 local zoneID = z[ZONE_ID_KEY]
6396 RemoveZone(zoneID)
6397end
6398
6399function DEV_MSG.DISARM_FAILED(value)
6400 local partitionID = value[PARTITION_KEY] + 1
6401 DisarmPartitionFailed(partitionID)
6402 --DisarmPartitionFailed(PartitionID, InterfaceID)
6403end
6404
6405function DEV_MSG.ARM_FAILED(value)
6406 local partitionID = value[PARTITION_KEY] + 1
6407 local nonce = value[NONCE_KEY]
6408 local description = value[DESCRIPTION_KEY]
6409 if string.find(string.lower(description), "bypass") then
6410 ArmPartitionFailed(partitionID, "bypass", nonce)
6411 elseif string.find(string.lower(description), "invalid") then
6412 ArmPartitionFailed(partitionID, "keypad", nonce)
6413 else
6414 ArmPartitionFailed(partitionID, "NA", nonce)
6415 end
6416 --ArmPartitionFailed(PartitionID, Action, InterfaceID)
6417end
6418
6419 end)
6420package.preload['device_specific_commands'] = (function (...)
6421--[[=============================================================================
6422 Copyright 2016 Control4 Corporation. All Rights Reserved.
6423===============================================================================]]
6424
6425-- This macro is utilized to identify the version string of the driver template version used.
6426if (TEMPLATE_VERSION ~= nil) then
6427 TEMPLATE_VERSION.device_specific_commands = "2016.01.08"
6428end
6429
6430--[[=============================================================================
6431 ExecuteCommand Code
6432
6433 Define any functions for device specific commands (EX_CMD.<command>)
6434 received from ExecuteCommand that need to be handled by the driver.
6435===============================================================================]]
6436--function EX_CMD.NEW_COMMAND(tParams)
6437-- LogTrace("EX_CMD.NEW_COMMAND")
6438-- LogTrace(tParams)
6439--end
6440 end)
6441package.preload['driver_functions'] = (function (...)
6442--[[=============================================================================
6443 File for implementing driver specific functions
6444
6445 Copyright 2015 Control4 Corporation. All Rights Reserved.
6446===============================================================================]]
6447require "partition_proxy.securitypartition"
6448
6449if (TEMPLATE_VERSION ~= nil) then
6450 TEMPLATE_VERSION.device_specific_commands = "2014.10.13"
6451end
6452
6453function SecurityPartitionFunctions.UserFunction1(PartitionID, DeviceID)
6454 DisplayPartitionText(PartitionID, "User Function1 Called")
6455end
6456
6457function SecurityPartitionFunctions.UserFunction2(PartitionID, DeviceID)
6458 DisplayPartitionText(PartitionID, "User Function2 Called")
6459end end)
6460package.preload['properties'] = (function (...)
6461--[[=============================================================================
6462 Properties Code
6463
6464 Copyright 2016 Control4 Corporation. All Rights Reserved.
6465===============================================================================]]
6466
6467-- This macro is utilized to identify the version string of the driver template version used.
6468if (TEMPLATE_VERSION ~= nil) then
6469 TEMPLATE_VERSION.properties = "2016.01.08"
6470end
6471
6472function ON_PROPERTY_CHANGED.SampleProperty(propertyValue)
6473
6474end
6475 end)
6476package.preload['common.c4_command'] = (function (...)
6477--[[=============================================================================
6478 Functions for handling and executing commands and actions
6479
6480 Copyright 2016 Control4 Corporation. All Rights Reserved.
6481===============================================================================]]
6482require "common.c4_driver_declarations"
6483
6484-- Set template version for this file
6485if (TEMPLATE_VERSION ~= nil) then
6486 TEMPLATE_VERSION.c4_command = "2016.01.08"
6487end
6488
6489--[[=============================================================================
6490 ExecuteCommand(sCommand, tParams)
6491
6492 Description
6493 Function called by Director when a command is received for this DriverWorks
6494 driver. This includes commands created in Composer programming.
6495
6496 Parameters
6497 sCommand(string) - Command to be sent
6498 tParams(table) - Lua table of parameters for the sent command
6499
6500 Returns
6501 Nothing
6502===============================================================================]]
6503function ExecuteCommand(sCommand, tParams)
6504 LogTrace("ExecuteCommand(" .. sCommand .. ")")
6505 LogInfo(tParams)
6506
6507 -- Remove any spaces (trim the command)
6508 local trimmedCommand = string.gsub(sCommand, " ", "")
6509 local status, ret
6510
6511 -- if function exists then execute (non-stripped)
6512 if (EX_CMD[sCommand] ~= nil and type(EX_CMD[sCommand]) == "function") then
6513 status, ret = pcall(EX_CMD[sCommand], tParams)
6514 -- elseif trimmed function exists then execute
6515 elseif (EX_CMD[trimmedCommand] ~= nil and type(EX_CMD[trimmedCommand]) == "function") then
6516 status, ret = pcall(EX_CMD[trimmedCommand], tParams)
6517 elseif (EX_CMD[sCommand] ~= nil) then
6518 QueueCommand(EX_CMD[sCommand])
6519 status = true
6520 else
6521 LogInfo("ExecuteCommand: Unhandled command = " .. sCommand)
6522 status = true
6523 end
6524
6525 if (not status) then
6526 LogError("LUA_ERROR: " .. ret)
6527 end
6528
6529 return ret -- Return whatever the function returns because it might be xml, a return code, and so on
6530end
6531
6532--[[=============================================================================
6533 EX_CMD.LUA_ACTION(tParams)
6534
6535 Description
6536 Function called for any actions executed by the user from the Actions Tab
6537 in Composer.
6538
6539 Parameters
6540 tParams(table) - Lua table of parameters for the command option
6541
6542 Returns
6543 Nothing
6544===============================================================================]]
6545function EX_CMD.LUA_ACTION(tParams)
6546 if (tParams ~= nil) then
6547 for cmd, cmdv in pairs(tParams) do
6548 if (cmd == "ACTION" and cmdv ~= nil) then
6549 local status, err = pcall(LUA_ACTION[cmdv], tParams)
6550 if (not status) then
6551 LogError("LUA_ERROR: " .. err)
6552 end
6553 break
6554 end
6555 end
6556 end
6557end
6558
6559--[[=============================================================================
6560 ReceivedFromProxy(idBinding, sCommand, tParams)
6561
6562 Description
6563 Function called for any actions executed by the user from the Actions Tab
6564 in Composer.
6565
6566 Parameters
6567 idBinding(int) - Binding ID of the proxy that sent a BindMessage to the
6568 DriverWorks driver.
6569 sCommand(string) - Command that was sent
6570 tParams(table) - Lua table of received command parameters
6571
6572 Returns
6573 Nothing
6574===============================================================================]]
6575function ReceivedFromProxy(idBinding, sCommand, tParams)
6576
6577 if (sCommand ~= nil) then
6578
6579 -- initial table variable if nil
6580 if (tParams == nil) then
6581 tParams = {}
6582 end
6583
6584 LogTrace("ReceivedFromProxy(): " .. sCommand .. " on binding " .. idBinding .. "; Call Function PRX_CMD." .. sCommand .. "()")
6585 LogInfo(tParams)
6586
6587 if ((PRX_CMD[sCommand]) ~= nil) then
6588 local status, err = pcall(PRX_CMD[sCommand], idBinding, tParams)
6589 if (not status) then
6590 LogError("LUA_ERROR: " .. err)
6591 end
6592 else
6593 LogInfo("ReceivedFromProxy: Unhandled command = " .. sCommand)
6594 end
6595 end
6596end
6597
6598--[[
6599 This function is called when a UI (Navigator) requests data, and
6600 calls the function requested.
6601--]]
6602function UIRequest(sRequest, tParams)
6603 local ret = ""
6604
6605 if (sRequest ~= nil) then
6606 tParams = tParams or {} -- initial table variable if nil
6607 LogTrace("UIRequest(): " .. sRequest .. "; Call Function UI_REQ." .. sRequest .. "()")
6608 LogInfo(tParams)
6609
6610 if (UI_REQ[sRequest]) ~= nil then
6611 ret = UI_REQ[sRequest](tParams)
6612 else
6613 LogWarn("UIRequest: Unhandled request = " .. sRequest)
6614 end
6615 end
6616
6617 return ret
6618end
6619 end)
6620package.preload['qolsys_helper'] = (function (...)
6621--
6622-- Qolsys helper variables and functions
6623--
6624VERSION_KEY = 'version'
6625SOURCE_KEY = 'source'
6626PARTITION_KEY = 'partition_id'
6627ACTION_KEY = 'action'
6628ARMING_VAL = 'ARMING'
6629INFO_VAL = 'INFO'
6630INFO_TYPE_KEY = 'info_type'
6631ZONE_EVENT_VAL = 'ZONE_EVENT'
6632ARMING_TYPE_KEY = 'arming_type'
6633ZONE_EVENT_TYPE_KEY = 'zone_event_type'
6634DISARM_VAL = 'DISARM'
6635ARM_AWAY_VAL = 'ARM_AWAY'
6636ARM_STAY_VAL = 'ARM_STAY'
6637SUMMARY_VAL = 'SUMMARY'
6638PARTITION_LIST_KEY = 'partition_list'
6639PARTITION_STATUS_KEY = 'status'
6640SCOPE_KEY = 'scope'
6641EVENT_KEY = 'event'
6642ID_KEY = 'id'
6643ZONE_KEY = 'zone'
6644ZONE_ID_KEY = 'zone_id'
6645ZONE_LIST_KEY = 'zone_list'
6646ZONE_STATUS_KEY = 'status'
6647ZONE_OPEN_VAL = 'Open'
6648ZONE_CLOSED_VAL = 'Closed'
6649ZONE_ACTIVE_VAL = 'Active'
6650ZONE_ACTIVATED_VAL = 'Activated'
6651ZONE_IDLE_VAL = 'Idle'
6652ZONE_NORMAL_VAL = 'Normal'
6653ZONE_NAME_KEY = 'name'
6654ZONE_TYPE_KEY = 'zone_type'
6655ALARM_VAL = 'ALARM'
6656ALARM_TYPE_KEY = 'alarm_type'
6657EXIT_DELAY_KEY = 'exit_delay'
6658ENTRY_DELAY_KEY = 'entry_delay'
6659DELAY_KEY = 'delay'
6660USERCODE_KEY = 'usercode'
6661FIRE_VAL = 'FIRE'
6662POLICE_VAL = 'POLICE'
6663AUX_VAL = 'AUXILIARY'
6664TOKEN_KEY = 'token'
6665ERROR_TYPE_KEY = 'error_type'
6666NONCE_KEY = 'nonce'
6667BYPASS_KEY = 'bypass'
6668DESCRIPTION_KEY = 'description'
6669CODE_REQUIRED = 'code_required'
6670SECURE_ARM_KEY = 'secure_arm'
6671VALUE_KEY = 'value'
6672
6673function ApplyQolsysFields(payload)
6674 payload[VERSION_KEY] = 1
6675 payload[SOURCE_KEY] = 'C4'
6676 payload[TOKEN_KEY] = Properties["Token"]
6677end
6678
6679function RequestSummary(nonce)
6680 --'{"partition_id": 0, "action": "INFO", "info_type": "SUMMARY", "version": 0, "source": "C4"}'
6681 local ReqInfo = {}
6682 ReqInfo[NONCE_KEY] = nonce or ""
6683 ReqInfo[ACTION_KEY] = INFO_VAL
6684 ReqInfo[INFO_TYPE_KEY] = SUMMARY_VAL
6685 ApplyQolsysFields(ReqInfo)
6686 SendCommand(C4:JsonEncode(ReqInfo))
6687end
6688
6689QolsysZoneTypes = {
6690UNKNOWN = 0,
6691CONTACT = 1,
6692MOTION = 2,
6693SOUND = 3 ,
6694BREAKAGE = 4,
6695SMOKE_HEAT = 5,
6696CARBON_MONOXIDE = 6,
6697RADON = 7,
6698TEMPERATURE = 8,
6699PANIC_BUTTON = 9,
6700CONTROL = 10,
6701CAMERA = 11,
6702LIGHT = 12,
6703GPS = 13,
6704SIREN = 14,
6705WATER = 15,
6706TILT = 16,
6707FREEZE = 17,
6708TAKEOVER_MODULE = 18,
6709GLASSBREAK = 19,
6710TRANSLATOR = 20,
6711MEDICAL_PENDANT = 21,
6712WATER_IQ_FLOOD = 22,
6713WATER_OTHER_FLOOD = 23,
6714IMAGE_SENSOR = 30,
6715WIRED_SENSOR = 100,
6716RF_SENSOR = 101,
6717KEYFOB = 102,
6718WALLFOB = 103,
6719RF_KEYPAD = 104,
6720PANEL = 105,
6721WTTS_OR_SECONDARY = 106,
6722SHOCK = 107,
6723SHOCK_SENSOR_MULTI_FUNCTION = 108,
6724DOOR_BELL = 109,
6725CONTACT_MULTI_FUNCTION = 110,
6726SMOKE_MULTI_FUNCTION = 111,
6727TEMPERATURE_MULTI_FUNCTION = 112,
6728SHOCK_OTHERS = 113,
6729OCCUPANCY_SENSOR = 114,
6730BLUETOOTH = 115,
6731PANEL_GLASS_BREAK = 116,
6732POWERG_SIREN = 117,
6733BLUETOOTH_SPEAKER = 118,
6734PANEL_MOTION = 119,
6735ZWAVE_SIREN = 120,
6736COUNT = 121 } -- yada
6737 end)
6738package.preload['sectemplate_communicator'] = (function (...)
6739--[[=============================================================================
6740 Template code for a Security Panel/Partition serial driver
6741
6742 Copyright 2015 Control4 Corporation. All Rights Reserved.
6743===============================================================================]]
6744require "device_messages"
6745require "connections"
6746require "qolsys_helper"
6747
6748
6749if (TEMPLATE_VERSION ~= nil) then
6750 TEMPLATE_VERSION.sectemplate_communicator = "2015.07.07"
6751end
6752
6753
6754--[[=============================================================================
6755 Interface routines required by the SecurityPanel code
6756===============================================================================]]
6757
6758--[[=============================================================================
6759 Re-read information from the panel hardware about partitions and zones
6760===============================================================================]]
6761function SecCom_ReadPanelInfo()
6762 local SerCmdStr = "SecCom_ReadPanelInfo"
6763
6764 -- TODO: Fill in SerCmdStr with the command to query the security panel
6765 RequestSummary("manual")
6766 --SendCommand(SerCmdStr)
6767end
6768
6769--[[=============================================================================
6770 Input from an acknowledgement required has been given so the driver can now
6771 process that input so the panel can move into a DISARMER_READY state.
6772===============================================================================]]
6773function SecCom_SendAcknowledgement(PartitionIndex, Input, InterfaceID)
6774 local SerCmdStr = "SecCom_SendAcknowledgement"
6775
6776 -- TODO: Fill in SerCmdStr with the command to acknowledge the state with the security panel
6777 SendCommand(SerCmdStr)
6778end
6779
6780--[[=============================================================================
6781 Convert the given date/time parameters into the format required by the security panel
6782===============================================================================]]
6783function SecCom_SendDateAndTime(TargYear, TargMonth, TargDay, TargHour, TargMinute, TargSecond, InterfaceID)
6784 local SerCmdStr = "SecCom_SendDateAndTime"
6785
6786 -- TODO: Fill in SerCmdStr with the command to set the time and date on the panel
6787 SendCommand(SerCmdStr)
6788end
6789
6790--[[=============================================================================
6791 Convert the given zone parameters into the format required by the panel
6792===============================================================================]]
6793function SecCom_SendSetZoneInfo(ZoneID, ZoneName, ZoneType, InterfaceID)
6794 local SerCmdStr = "SecCom_SendSetZoneInfo"
6795
6796 -- TODO:
6797 -- Fill in SerCmdStr with the command to set attributes of the specified zone
6798 -- This may require multiple calls to the panel in order to affect the different parameters
6799 SendCommand(SerCmdStr)
6800end
6801
6802--[[=============================================================================
6803 Tell the panel to activate or de-activate a specific partition
6804
6805 Note: Not all panels will allow this
6806===============================================================================]]
6807function SecCom_SendPartitionEnabled(PartitionIndex, IsEnabled, InterfaceID)
6808 local SerCmdStr = "SecCom_SendPartitionEnabled"
6809
6810 -- TODO:
6811 -- Fill in SerCmdStr with the command to enable or unenable the specified partition
6812 -- according to the IsEnabled parameter.
6813 SendCommand(SerCmdStr)
6814end
6815
6816--[[=============================================================================
6817 Interface routines required by the SecurityParition code
6818===============================================================================]]
6819--[[=============================================================================
6820 Convert the given arm parameters into the format required by the panel
6821===============================================================================]]
6822function SecCom_SendArmPartition(PartitionIndex, ArmType, UserCode, Bypass, InterfaceID)
6823 RequestArm(PartitionIndex, ArmType, UserCode, Bypass, InterfaceID)
6824end
6825
6826--[[=============================================================================
6827 Convert the given disarm parameters into the format required by the panel
6828===============================================================================]]
6829function SecCom_SendDisarmPartition(PartitionIndex, UserCode, InterfaceID)
6830 RequestDisarm(PartitionIndex, UserCode, InterfaceID)
6831end
6832
6833--[[=============================================================================
6834 Cancel a pending ARM command
6835===============================================================================]]
6836function SecCom_ArmCancel(PartitionIndex, InterfaceID)
6837 local SerCmdStr = "SecCom_ArmCancel"
6838
6839 -- TODO: Fill in SerCmdStr with the command to cancel a pending arm command
6840 SendCommand(SerCmdStr)
6841end
6842
6843--[[=============================================================================
6844 Convert the given emergency parameters into the format required by the panel
6845===============================================================================]]
6846function SecCom_SendExecuteEmergency(PartitionIndex, EmergencyType, InterfaceID)
6847 RequestAlarm(PartitionIndex, EmergencyType, InterfaceID)
6848end
6849
6850--[[=============================================================================
6851 Send a single key press from the UI keypad to the hardware
6852===============================================================================]]
6853function SecCom_SendKeyPress(PartitionIndex, KeyValue, InterfaceID)
6854 local SerCmdStr = "SecCom_SendKeyPress"
6855
6856 -- TODO: Fill in SerCmdStr with the command to indicate that a single key was pressed on the keypad
6857 SendCommand(SerCmdStr)
6858end
6859
6860
6861--[[=============================================================================
6862 Process additional info that was requested from one of the partition UIs
6863===============================================================================]]
6864function SecCom_ProcessAdditionalInfo(PartitionIndex, InfoString, NewInfo, FunctionName, InterfaceID)
6865 -- TODO: Inspect the FunctionName variable to determine which routine should be called
6866 -- from here; usually this would be to complete the action that required additional
6867 -- info to be completed
6868
6869 --if(FunctionName == "Function1Handler") then Function1Handler(PartitionIndex, InfoString, NewInfo, InterfaceID)
6870 --else if(FunctionName == "Function2Handler") then Function2Handler(PartitionIndex, InfoString, NewInfo, InterfaceID)
6871 --else
6872 LogWarn("Unhandled Additional Info Function: >>>%s<<< Info: >>%s<< New Info >>%s<<", FunctionName, InfoString, NewInfo)
6873 --end
6874end
6875
6876
6877--[[=============================================================================
6878 Process additional info that was requested from the panel UI
6879===============================================================================]]
6880function SecCom_ProcessAdditionalPanelInfo(InfoString, NewInfo, FunctionName, InterfaceID)
6881 -- TODO: Inspect the FunctionName variable to determine which routine should be called
6882 -- from here; usually this would be to complete the action that required additional
6883 -- info to be completed
6884
6885 --if(FunctionName == "PanelFunction1Handler") then PanelFunction1Handler(PartitionIndex, InfoString, NewInfo, InterfaceID)
6886 --else if(FunctionName == "PanelFunction2Handler") then PanelFunction2Handler(PartitionIndex, InfoString, NewInfo, InterfaceID)
6887 --else
6888 LogWarn("Unhandled Panel Additional Info Function: >>>%s<<< Info: >>%s<< New Info >>%s<<", FunctionName, InfoString, NewInfo)
6889 --end
6890end
6891
6892--[[=============================================================================
6893 Received a Confirmation command
6894===============================================================================]]
6895function SecCom_SendConfirmation(PartitionIndex, InterfaceID)
6896-- LogTrace("SecCom_SendConfirmation for Partition %d InterfaceID: %s", tonumber(PartitionIndex), InterfaceID)
6897end
6898
6899--[[=============================================================================
6900 The following functions are called from the PgmInfo code
6901===============================================================================]]
6902--[[=============================================================================
6903 Send the open control command to the specified zone
6904===============================================================================]]
6905function SecCom_SendPgmControlOpen(PgmID)
6906 local SerCmdStr = "SecCom_SendPgmControlOpen"
6907
6908 -- TODO: Fill in SerCmdStr with the command to open the specified pgm
6909 SendCommand(SerCmdStr)
6910end
6911
6912--[[=============================================================================
6913 Send the closed control command to the specified pgm
6914===============================================================================]]
6915function SecCom_SendPgmControlClose(PgmID)
6916 local SerCmdStr = "SecCom_SendPgmControlClose"
6917
6918 -- TODO: Fill in SerCmdStr with the command to close the specified pgm
6919 SendCommand(SerCmdStr)
6920end
6921
6922--[[=============================================================================
6923 Send the control command to toggle the current state of the specified pgm
6924===============================================================================]]
6925function SecCom_SendPgmControlToggle(PgmID)
6926 local SerCmdStr = "SecCom_SendPgmControlToggle"
6927
6928 -- TODO: Fill in SerCmdStr with the command to toggle the specified pgm
6929 SendCommand(SerCmdStr)
6930end
6931
6932
6933
6934------------------------------------------------------------------------------------
6935-- TODO: Create functions to handle above function calls
6936------------------------------------------------------------------------------------
6937function RequestArm(PartitionIndex, ArmType, UserCode, Bypass, InterfaceID)
6938 --'{"partition_id": 0, "action": "ARMING", "arming_type": "ARM_STAY", "version": 0, "usercode": "1234", "source": "C4"}'
6939 local SerCmdStr = {}
6940 ApplyQolsysFields(SerCmdStr)
6941 SerCmdStr[PARTITION_KEY] = PartitionIndex - 1
6942 SerCmdStr[ACTION_KEY] = ARMING_VAL
6943 if string.find(string.lower(ArmType), "stay") then
6944 SerCmdStr[ARMING_TYPE_KEY] = ARM_STAY_VAL
6945 elseif string.find(string.lower(ArmType), "away") then
6946 SerCmdStr[ARMING_TYPE_KEY] = ARM_AWAY_VAL
6947 end
6948 SerCmdStr[USERCODE_KEY] = UserCode
6949 SerCmdStr[CODE_REQUIRED] = IsCodeRequiredToArm(PartitionIndex)
6950 SerCmdStr[BYPASS_KEY] = Bypass
6951 --Away Instant,Stay Instant
6952 if string.find(string.lower(ArmType), "instant") then
6953 SerCmdStr[DELAY_KEY] = 0
6954 end
6955 SerCmdStr[NONCE_KEY] = InterfaceID
6956 SendCommand(C4:JsonEncode(SerCmdStr))
6957end
6958
6959function RequestDisarm(PartitionIndex, UserCode, InterfaceID)
6960 --'{"partition_id": 0, "action": "ARMING", "arming_type": "DISARM", "version": 0, "usercode": "1234", "source": "C4"}'
6961 local SerCmdStr = {}
6962 ApplyQolsysFields(SerCmdStr)
6963 SerCmdStr[PARTITION_KEY] = PartitionIndex - 1
6964 SerCmdStr[ACTION_KEY] = ARMING_VAL
6965 SerCmdStr[ARMING_TYPE_KEY] = DISARM_VAL
6966 SerCmdStr[USERCODE_KEY] = UserCode
6967 SerCmdStr[NONCE_KEY] = InterfaceID
6968 --SerCmdStr['InterfaceID'] = InterfaceID
6969 SendCommand(C4:JsonEncode(SerCmdStr))
6970end
6971
6972function RequestAlarm(PartitionIndex, EmergencyType, InterfaceID)
6973 local SerCmdStr = {}
6974 ApplyQolsysFields(SerCmdStr)
6975 SerCmdStr[PARTITION_KEY] = PartitionIndex - 1
6976 SerCmdStr[ACTION_KEY] = ALARM_VAL
6977 if(EmergencyType == 'Fire') then
6978 SerCmdStr[ALARM_TYPE_KEY] = FIRE_VAL
6979 elseif(EmergencyType == 'Police') then
6980 SerCmdStr[ALARM_TYPE_KEY] = POLICE_VAL
6981 elseif(EmergencyType == 'Panic') then
6982 SerCmdStr[ALARM_TYPE_KEY] = AUX_VAL
6983 else
6984 SerCmdStr[ALARM_TYPE_KEY] = EmergencyType
6985 end
6986 --SerCmdStr['InterfaceID'] = InterfaceID
6987 SerCmdStr[NONCE_KEY] = InterfaceID
6988 SendCommand(C4:JsonEncode(SerCmdStr))
6989end end)
6990--[[=============================================================================
6991 Template for Security Panel/Security Partition Driver
6992
6993 Copyright 2015 Control4 Corporation. All Rights Reserved.
6994===============================================================================]]
6995require "common.c4_driver_declarations"
6996require "common.c4_common"
6997require "common.c4_init"
6998require "common.c4_property"
6999require "common.c4_command"
7000require "common.c4_diagnostics"
7001require "common.c4_notify"
7002require "common.c4_utils"
7003
7004require "panel_proxy.securitypanel"
7005require "partition_proxy.securitypartition"
7006
7007require "actions"
7008require "driver_functions"
7009require "properties"
7010
7011if (TEMPLATE_VERSION ~= nil) then
7012 TEMPLATE_VERSION.driver = "2014.10.13"
7013end
7014
7015-- TODO:
7016-- Replace "sectemplate_communicator" with the name you have changed the
7017-- "sectemplate_communicatior.lua" file to.
7018require "sectemplate_communicator"
7019
7020-- TODO:
7021-- Make sure you set your DRIVER_NAME here
7022DRIVER_NAME = "Your Security System Driver Name"
7023
7024-- TODO:
7025-- This macro is utilized to identify the version string of the driver being deployed.
7026TEMPLATE_VERSION.security_system_template_version = "2014.09.11"
7027
7028-- NOTE:
7029-- The following properties should not have to be changed, but can be if the author feels it
7030-- is necessary. Great care should be taken when modifying these values though, since the
7031-- values correspond to other values contained from within other files in the project.
7032PANEL_PROXY_BINDINGID = DEFAULT_PROXY_BINDINGID
7033BASE_PARTITION_PROXY_BINDINGID = DEFAULT_PROXY_BINDINGID + 1
7034SERIAL_PORT_BINDINGID = 1
7035
7036-- TODO:
7037-- Set the number of partitions and zones supported by this driver.
7038PARTITION_ID_MAX = 2
7039
7040--[[==========================================================================================
7041 Initialization Code
7042============================================================================================]]
7043function ON_DRIVER_EARLY_INIT.MainDriver()
7044end
7045
7046function ON_DRIVER_INIT.MainDriver()
7047 -- TODO:
7048 -- Set your unique driver name here, so driver messages can be distinguished in the log files
7049 SetLogName("Your Security Driver Name")
7050
7051 --TODO:
7052 -- Change the default zone text if desired
7053 -- SetDefaultZoneName("Your Custome Zone Name")
7054 Initialize_SecuritySystem()
7055end
7056
7057function ON_DRIVER_LATEINIT.MainDriver()
7058end
7059
7060function Initialize_SecuritySystem()
7061 TheSecurityPanel = SecurityPanel:new(PANEL_PROXY_BINDINGID)
7062
7063 for PartitionIndex = 1, PARTITION_ID_MAX do
7064 SecurityPartition:new(PartitionIndex, BASE_PARTITION_PROXY_BINDINGID + (PartitionIndex - 1))
7065 end
7066
7067 TheSecurityPanel:Initialize()
7068
7069end