' ---------------------------------------------------------------- ' __ _ ' / _| | ' _ __ ___ _ _ ___ ___ | |_| |_ '| '_ ` _ \| | | | / __|/ _ \| _| __| '| | | | | | |_| |_\__ \ (_) | | | |_ '|_| |_| |_|\__, (_)___/\___/|_| \__| ' __/ |http://myDotSoft.com ' |___/ ' ---------------------------------------------------------------- ' title : my.Elk ' filename : my.elk.txt ' version : 2.3 ' description : Monitor & control the Elk M1 Gold & M1 EZ8 alarm panel ' author : Dan (electron) ' email : dan at marstracker dot org ' ---------------------------------------------------------------- ' DISCLAIMER ' ---------------------------------------------------------------- ' my.Elk is freeware, however, it is NOT in the Public Domain and ' my.Soft retains the copyright for this work. You can use my.Elk ' freely, but you can not claim it as your own creation, or charge ' others money for it. ' Use my.Elk at your own risk. This script is provided for your own ' personal use, as is. Any commercial use is strictly prohibited. ' my.Soft, or the author, shall not be held responsible for any ' results allegedly caused by use (or misuse) of this script. ' ---------------------------------------------------------------- ' Instructions : ' [1] Copy this script to the Homeseer scripts directory ' [2] Configure the serial port by updating the iSerialPort ' variable in the 'User definable settings' section. ' [3] If you want to 'monitor' your M1, set 'bReceive' to TRUE ' in the 'User definable settings' section. ' [4] create my.elk.ini in the config directory, use the following ' example as your template: ' --- begin my.elk.ini --- ' [my.elk.zones] ' zone001=x1 ' zone002=x2 ' ' [my.elk.voltages] ' zone008=x20 ' ' [my.elk.outputs] ' output003=x33 ' ' [my.elk.keypad.keys] ' keypad01:F1=[event:elk keypad test] ' ' [my.elk.keypad.temp] ' keypad01=x19 ' ' [my.elk.tasks] ' task02=x44 ' ' [my.elk.thermostat.01] ' mode=y1 ' hold=y2 ' fan=y3 ' temp=y4 ' heat=y5 ' cool=y6 ' humidity=y7 ' ' [my.elk.thermostat.02] ' temp=z1 ' ' [my.elk.thermostat.03] ' temp=z10 ' humidity=z11 ' ' --- end my.elk.ini --- ' [5] When you want to start monitoring your Elk M1, execute ' my.elk.txt("OpenSerialPort","") to process incoming events, and ' use my.elk.txt("CloseSerialPort","") to release the COM port ' (in case you need to use Elk RP). You could use a palmpad ' or other device to open/close the port, making working with RP ' much easier. No Homeseer restart is required. ' Tip: add these start & stop calls to startup.txt and shutdown.txt ' so Homeseer will automatically monitor the Elk. ' [6] For more information on how to use the my.Elk commands, scroll down ' to the 'Documentation' section. ' ---------------------------------------------------------------- ' changelog: ' ' version 2.3 (05032006): added a large documentation section, made some changes ' to the logging routine, implemented minor fixes. ' version 2.2 (05012006): added support for arming and disarming ' the system, see command list below for more ' info, also added support for reading ' data from thermostats, added the ' thermostat_update command, and also cleaned ' up some code. See the my.elk.ini template ' above to learn how to map your thermostats. ' ' version 2.1 (01042006): added delay to the sync command, in case ' the serial port isn't responding instantly. ' Also added support for syncronizing the ' control outputs. Changed some logic in the ' zones & outputs syncronizing, so it will only ' force a virtual device update if the status ' of the zone or output is not the same. ' my.Elk can now detect when ElkRP is connected ' to the M1. ' version 2.0 (12202005): BETA status removed ' version 2.0 (09292005): *** BETA *** ' added support for auto syncing: bAutoSync ' my.Elk can now process ASCII strings sent ' directly by the elk, using th RAW:COMMAND ' syntax. See below for more information. ' version 1.9 (09282005): added the zs command to request zone status ' updates, internal changes, added support for ' a complete zone update, which is triggered ' when issuring the zs command (useful to keep ' Homeseer in sync with the M1 after a PC reboot ' version 1.8 (08282005): switched to an INI file, ' trigger Homeseer events when a keypad button ' has been pressed ' version 1.7 (08232005): added the bAutoOpen variable, new keypad_temp ' function to request a keypad temp reading, and ' added support for keypad temperature monitoring. ' Events can be triggered based on temperature by ' using the "Device Value Change" trigger. ' version 1.6 (08222005): bug fixes ' version 1.5 (08112005): added support for monitoring of zones, tasks, ' voltages and outputs ' version 1.2 (08102005): added voltage_request ' version 1.1 (08092005): added user definable settings ' version 1.0 (08082005): added thermostat_set, customvalue_write, keypad_press, ' plc_raw, speak_phrase and speak_word ' version 0.9 (08072005): first release ' ---------------------------------------------------------------- ' Public iSerialPort,bDebug,bAutoSync,bAutoOpen,bSimulate,bReceive,sINIfile ' ' ---------------------------------------------------------------- ' User definable settings ' ---------------------------------------------------------------- ' ' ini config file sINIfile = "my.elk.ini" ' ' select serial port iSerialPort = 2 ' ' automatically attempt to open the serial port when sending commands bAutoOpen = true ' ' enable debug level logging (WARNING: this can be very CPU intensive) bDebug = false ' ' enable simulation (no data will be sent to the serial port, useful for debugging) bSimulate = false ' ' enable/disable processing of incoming data bReceive = true ' ' enable/disable automatically synchronizing the M1 on connect. bAutoSync = true ' ' ---------------------------------------------------------------- ' documentation ' ---------------------------------------------------------------- ' ' RAW commands ' description: RAW commands allows the Elk M1 to control certain aspects of Homeseer directly, ' without requiring an output or such to trigger this action. You put the command ' in an ASCII string, and create a rule to send it to the main serial port (0). ' A RAW command allows you to use the Homeseer TTS engine to make announcements, ' control an event, or control a device. ' usage: 'raw:speak:phrase' ' 'raw:event:name' ' 'raw:device:code' ' example: raw:event:bedroom lights on ' => run the 'bedroom lights on' event ' raw:device:a2:on ' => turn A2 on (through Homeseer) ' raw:speak:take the trash out ' => announce "take the trash out' using the default Homeseer TTS voice ' ' my.Elk commands ' ' [openserialport] ' description: open the serial port manually ' usage: my.elk.txt("openserialport","") ' ' [closeserialport] ' description: close the serial port manually ' usage: my.elk.txt("closeserialport","") ' ' [arm] ' description: arm the system ' usage: my.elk.txt("arm","area|arming level|user code") ' * 'area' has to be in the 1 - 8 range ' * valid arming levels: ' 1 = armed away, 2= armed stay, 3=armed stay instant, ' 4 = armed night, 5 = armed night instant, 6 = armed vacation, ' 7 = armed to next away mode, 8 = arm to next stay mode ' example: my.elk.txt("arm","1|3|1234") ' => arm area 1, in armed stay intant mode, using user code 1234 ' ' [disarm] ' description: disarm the system ' usage: my.elk.txt("disarm","area|user code") ' * 'area' has to be in the 1 - 8 range ' example: my.elk.txt("disarm","1|1234") ' => disarm area 1, using user code 1234 ' ' [cs] ' description: request a status update of all 208 outputs, no parameters are required ' usage: my.elk.txt("cs","") ' example: my.elk.txt("cs","") ' ' [zs] ' description: request a status update of all 208 inputs (zones), no parameters are required ' usage: my.elk.txt("zs","") ' example: my.elk.txt("zs","") ' ' [customvalue_write] ' description: change a stored custom value ' usage: my.elk.txt("customvalue_write","custom value #|data|time") ' * 'custom value #' has to be in the 1 - 20 range ' * 'data' can be up to 5 digits ' * 'time' is an optional boolean, change it to TRUE if the data is a timestamp ' example: my.elk.txt("customvalue_write","1|2140|TRUE") ' => write "2140" custom value #1, data is a timestamp ' ' [keypad_temp] ' description: request a temperature reading from a keypad ' usage: my.elk.txt("keypad_temp","keypad") ' * 'keypad' has to be in the 1 - 16 range ' example: my.elk.txt("keypad_temp","1") ' => request a temperature reading for keypad 1 ' ' [keypad_press] ' description: simulate a keypad function key press ' usage: my.elk.txt("keypad_press","keypad #|key") ' * 'keypad #' has to be in the 1 - 16 range ' * 'key' is either in the 1 - 6 range (F keys), or can be *, or C ' example: my.elk.txt("keypad_press","1|4") ' => simulate pressing the F4 key on Keypad 1 ' ' [plc_raw] ' description: provides full control of a PLC device ' usage : my.elk.txt("plc_raw","house code|unit code|timer|command|parameter") ' * 'timer' defines how long (in seconds) the device will stay on (0-9999), 0 = don't turn off ' * 'command' is a numerical value, valid commands are: ' 1 = all units off, 2 = all lights on, 3 = unit on, 4 = unit off, 5 = dim (parameter holds number of dims), ' 6 = bright (parameter holds number of brights), 7 = all lights off, 8 = extended code, ' 9 = preset dim (parameter holds dim level, 0 - 99%), 10 = extended data, 11 = status request, 12 = hail request ' example: my.elk.txt("plc_raw","A|2|15|6|4") ' => send 4 bright commands to A2, turn A2 off after 15 seconds ' ' [speak_word] ' description: speak word at voice/sirene output ' usage: my.elk.txt("speak_word","word #") ' * see documentation for a table which lists all words ' example: my.elk.txt("speak_word","103") ' => speak word 103 ' ' [speak_phrase] ' description: speak phrase at voice/sirene output ' usage: my.elk.txt("speak_phrase","phrase #") ' * see documentation for a table which lists all phrases ' example: my.elk.txt("speak_phrase","56") ' => speak phrase 56 ' ' [output_toggle] ' description: toggle an output ' usage: my.elk.txt("output_toggle","output #") ' example: my.elk.txt("output_toggle","3") ' => toggle output #3 ' ' [output_off] ' description: turn output off ' usage: my.elk.txt("output_off","output #") ' example: my.elk.txt("output_off","3") ' => turn output #3 off ' ' [output_on] ' description: turn output on (for x seconds) ' usage: my.elk.txt("output_on","output #|seconds") ' 'seconds' is a value between 0 and 9999, 0 = don't turn output off ' example: my.elk.txt("output_on","3|4") ' => turn output 3 on for 4 seconds ' ' [activate_task] ' description: activate a task ' usage: my.elk.txt("task_activate","task #") ' example: my.elk.txt("task_activate","14") ' => activate task #14 ' ' [voltage_request] ' description: request a voltage reading for an input/zone ' usage: my.elk.txt("voltage_request","zone #") ' example: my.elk.txt("voltage_request","8") ' => requests a voltage reading for input #8 ' ' [plc_toggle] ' description: toggle a PLC device ' usage: my.elk.txt("plc_toggle","house code|unit code") ' example: my.elk.txt("plc_toggle","A|2") ' => toggle A2 ' ' [plc_off] ' description: turn PLC device off ' usage: my.elk.txt("plc_off","house code|unit code") ' example: my.elk.txt("plc_off","A|2") ' => turn A2 off ' ' [plc_on] ' description: turn PLC device on ' usage: my.elk.txt("plc_on","house code|unit code") ' example: my.elk.txt("plc_on","D|5") ' => turn D5 on ' ' [display_message] ' description: display message on keypad ' usage: my.elk.txt("display_message","area|clear|beep|timeout|line 01|line02") ' * 'area' has to be in the 1 - 8 range ' * valid 'clear' options are: 0 = clear message, 1 = clear message with & key, 2 = display until timeout ' * 'beep' can be 0 (no beep) or 1 (beep) ' * use ^ in 'line01' to start a new line ' * 'timeout' has to be in the 0 - 65535 range (0 = no timeout) ' example: my.elk.txt("display_message","1|2|1|15|Take out trash!") ' => display "Take out trash!" for 15 seconds on keypad in area 1, while also beeping ' ' [thermostat_set] ' description: adjust thermostat settings ' usage my.elk.txt("thermostat_set","thermostat #|option|value") ' * valid 'option' values are: ' 0 = Mode value = [00=Off,01=Heat,02=Cool,03=Auto, 04=Emergency Heat] ' 1 = Hold value = [Hold current temperature. 00=False, 01=True] ' 2 = Fan value = [00=Fan Auto, 01=Fan turned on] ' 3 = not used ' 4 = CoolSetPoint value = [Cool setpoint If in Cool/auto mode, 01 to 99] ' 5 = HeatSetPoint value = [Heat setpoint If in heat/auto mode, 01 to 99] ' example: my.elk.txt("thermostat_set","1|4|72") ' => change thermostat 1's cool set point to 72 degrees ' ' [thermostat_update] ' description: request an update from thermostat ' usage: my.elk.txt("thermostat_update","thermostat #") ' example: my.elk.txt("thermostat_update","3") ' => request an update from thermostat #3 ' ' ---------------------------------------------------------------- ' Do not modify anything below unless you know what you are doing! ' ---------------------------------------------------------------- Sub serial_processor(sData) If bReceive = False Then d "the bReceive boolean has been set to false, no data will be processed" Exit Sub End If iTimerStart = Timer If Len(sData) > 3 Then If mid(sData,1,3) = "raw" Then d "serial_processor(): RAW command received: " & sData If Instr(1,sData,vbCr,1) Then sData = Mid(sData,1,Instr(1,sData,vbCr,1)-1) End If raw_processor mid(sData,5) Else sLEN = mid(sData,1,2) sCRC = mid(sData,Len(sData) - 1) sSTR = mid(sData,3,Len(sData) - 4) iCRC = sCRC iCRC = VerifyCRC(sLEN,sSTR,iCRC) If iCRC = 0 AND Len(sSTR) + 2 = Cint("&H" & sLEN) Then d "serial_processor(): " & sData & " => LEN=" & sLEN & " DATA=" & sSTR & " CRC=" & sCRC & " MSG OK" Select Case mid(sSTR,1,2) Case "CS" outputstatus_processor mid(sSTR,3) Case "ZS" zonestatus_processor mid(sSTR,3) Case "ZC" zone_processor mid(sSTR,3,3),mid(sSTR,6,1) Case "CC" output_processor mid(sSTR,3,3),mid(sSTR,6,1) Case "TC" task_processor Cint(mid(sSTR,3,3)) Case "ST" keypadTemp_processor mid(sSTR,4,2),Cint(mid(sSTR,6,3)) Case "ZV" zvolt_processor mid(sSTR,3,3),mid(sSTR,6,3) Case "KC" keypadKey_processor mid(sSTR,3,2), mid(sSTR,5,2) Case "RP" If mid(sSTR,3,2) = "01" Then rp_processor True If mid(sSTR,3,2) = "00" Then rp_processor False Case "TR" thermostat_processor mid(sSTR,3,2), mid(sSTR,5,1), mid(sSTR,6,1), mid(sSTR,7,1), mid(sSTR,8,2), mid(sSTR,10,2), mid(sSTR,12,2), mid(sSTR,14,2) Case Else d "serial_processor(): an unknown status update (" & mid(sSTR,1,2) & ") was received, update will be ignored" End Select Else d "serial_processor(): " & sData & " => LEN=" & sLEN & " DATA=" & sSTR & " CRC=" & sCRC & " MSG CORRUPT" End If End If End If iTimerStop = Timer iExecutionTime = iTimerStop - iTimerStart d "serial_processor() execution time: " & iExecutionTime & " seconds" End Sub ' ' ---------------------------------------------------------------- ' not completed / beta ' ---------------------------------------------------------------- ' Sub thermostat_processor(sThermostat,iMode,iHold,iFan,iTemp,iHeat,iCool,iHumidity) Select Case iMode Case 0 sMode = "Off" Case 1 sMode = "Heat" Case 2 sMode = "Cool" Case 3 sMode = "Auto" Case 4 sMode = "Emergency Heat" End Select If iHold = 1 Then sHold = "True" Else sHold = "False" If iFan = 1 Then sFan = "True" Else sFan = "False" sTemp = iTemp & "F" sHeat = iHeat & "F" sCool = iCool & "F" sHumidity = iHumidity & "%" d "thermostat_processor(): received update from thermostat " & sThermostat & ": iMode=" & iMode & ", iHold=" & iHold & ", iFan=" & iFan & ", iTemp=" & iTemp & ", iHeat=" & iHeat & ", iCool=" & iCool & ", iHumidity=" & iHumidity myElkThermostat = hs.GetINIsection("my.elk.thermostat." & sThermostat,sINIfile) If myElkThermostat = "" Then e "thermostat_processor(): thermostat " & sThermostat & " has not been defined, ignoring update" Exit Sub End If sTmode = hs.GetINISetting("my.elk.thermostat." & sThermostat,"mode","",sINIfile) sThold = hs.GetINISetting("my.elk.thermostat." & sThermostat,"hold","",sINIfile) sTfan = hs.GetINISetting("my.elk.thermostat." & sThermostat,"fan","",sINIfile) sTtemp = hs.GetINISetting("my.elk.thermostat." & sThermostat,"temp","",sINIfile) sTheat = hs.GetINISetting("my.elk.thermostat." & sThermostat,"heat","",sINIfile) sTcool = hs.GetINISetting("my.elk.thermostat." & sThermostat,"cool","",sINIfile) sThumidity = hs.GetINISetting("my.elk.thermostat." & sThermostat,"humidity","",sINIfile) If sTmode <> "" Then hs.SetDeviceString sTmode, sMode, True Else d "thermostat_processor(): 'mode' has not been assigned to a device, ignoring update" If sThold <> "" Then hs.SetDeviceString sThold, sHold, True Else d "thermostat_processor(): 'hold' has not been assigned to a device, ignoring update" If sTfan <> "" Then hs.SetDeviceString sTfan, sFan, True Else d "thermostat_processor(): 'fan' has not been assigned to a device, ignoring update" If sTtemp <> "" Then hs.SetDeviceString sTtemp, sTemp, True Else d "thermostat_processor(): 'temp' has not been assigned to a device, ignoring update" If sTheat <> "" Then hs.SetDeviceString sTheat, sHeat, True Else d "thermostat_processor(): 'heat' has not been assigned to a device, ignoring update" If sTcool <> "" Then hs.SetDeviceString sTcool, sCool, True Else d "thermostat_processor(): 'cool' has not been assigned to a device, ignoring update" If sThumidity <> "" Then hs.SetDeviceString sThumidity, sHumidity, True Else d "thermostat_processor(): 'humidity' has not been assigned to a device, ignoring update" End Sub Sub arm(strParams) ' arm the system ' usage : my.elk.txt("arm","iArea|iLevel|sUserCode") ' example: my.elk.txt("arm","1|3|1234") ' ' If strParams <> "" Then myArray = split(strParams,"|") If uBound(myArray) <> 2 Then e "arm(): check syntax: 1 or more parameters have not been specified, exiting ..." Exit Sub Else sUserCode = myArray(2) iLevel = myArray(1) iArea = myArray(0) End If Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If d "arm(): attempting to arm area/partition " & iArea & ", arming level = " & iLevel & ", user code = " & sUserCode If Len(sUserCode) = 4 Then sUserCode = lPad(sUserCode,"0",6) ElseIf Len(sUserCode) = 6 Then sUserCode = sUserCode Else e "arm(): an invalid user code has been specified (user code can only be 4 or 6 characters), system will NOT be armed!" Exit Sub End If If iArea < 1 or iArea > 8 Then e "arm(): an invalid area/partition has been specified (range is 1-8), system will NOT be armed!" Exit Sub End If If iLevel < 1 or iLevel > 8 Then e "arm(): an invalid arming level has been specified (range is 1-8), system will NOT be armed!" Exit Sub End If Select Case iLevel Case 1 sLevel = "Armed Away" Case 2 sLevel = "Armed Stay" Case 3 sLevel = "Armed Stay Instant" Case 4 sLevel = "Armed Night" Case 5 sLevel = "Armed Night Instant" Case 6 sLevel = "Armed Vacation" Case 7 sLevel = "Armed to next Away Mode" Case 8 sLevel = "Armed to next Stay Mode" End Select d "arm(): arming area/partition " & iArea & " (" & sLevel & ")" sString = "a" & iLevel & iArea & sUserCode sString = generateString(sString) SendToElk sString End sub Sub disarm(strParams) ' disarm the system ' usage : my.elk.txt("disarm","iArea|sUserCode") ' example: my.elk.txt("disarm","1|1234") ' ' iArea = partition or area 1 - 8 ' If strParams <> "" Then myArray = split(strParams,"|") If uBound(myArray) <> 1 Then e "arm(): check syntax, 1 or more parameters have not been specified, exiting ..." Exit Sub Else sUserCode = myArray(1) iArea = myArray(0) End If Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If d "disarm(): attempting to disarm area/partition " & iArea & ", user code = " & sUserCode If Len(sUserCode) = 4 Then sUserCode = lPad(sUserCode,"0",6) ElseIf Len(sUserCode) = 6 Then sUserCode = sUserCode Else e "disarm(): an invalid user code has been specified (user code can only be 4 or 6 characters), system will NOT be disarmed!" Exit Sub End If If iArea < 1 or iArea > 8 Then e "disarm(): an invalid area/partition has been specified (range is 1-8), system will NOT be disarmed!" Exit Sub End If d "arm(): disarming area/partition " & iArea sString = "a" & "0" & iArea & sUserCode sString = generateString(sString) SendToElk sString End Sub Sub task_processor(iTask) d "task_processor(): this feature has not been finished yet" End Sub ' Sub thermostat_update(strParams) ' request update from thermostat ' usage: my.elk.txt("thermostat_update",sThermostat) ' example: my.elk.txt("thermostat_update","03") ' If Int(strParams) < 1 And Int(strParams) > 16 Then e "thermostat_update(): " & strParams & " is not a valid thermostat" Exit Sub Else sThermostat = lPad(strParams,"0",2) d "thermostat_update(): requesting update for thermostat " & sThermostat End If sString = "tr" & sThermostat & "00" sString = generateString(sString) SendToElk sString End Sub ' ' ---------------------------------------------------------------- ' control functions ' ---------------------------------------------------------------- Sub keypad_temp(strParams) ' request temperature reading ' usage : my.elk.txt("keypad_temp","iKeypad") ' example: my.elk.txt("keypad_temp","1") : Request temperature reading for keypad ' keypads: 1-16 ' If strParams <> "" Then iKeypad = lPad(strParams,"0",2) Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "st" & "1" & iKeypad & "00" sString = generateString(sString) SendToElk sString End Sub Sub keypad_press(strParams) ' simulate keypad function key press ' usage : my.elk.txt("keypad_press","iKeypad|sKey") ' example: my.elk.txt("keypad_press","1|4") : Simulate pressing the F4 key on Keypad 1 ' keypads: 1-16 ' keys : 1 - 6, *, C ' If strParams <> "" Then myArray = split(strParams,"|") iKeypad = lPad(myArray(0),"0",2) sKey = myArray(1) Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "kf" & iKeypad & sKey & "00" sString = generateString(sString) SendToElk sString End Sub Sub customvalue_write(strParams) ' change a stored custom value ' usage : my.elk.txt("customvalue_write","iValue|sData|bTime") ' example: my.elk.txt("customvalue_write","1|2140|TRUE") ' ' bTime: select TRUE If data is a timestamp ' If strParams <> "" Then myArray = split(strParams,"|") iValue = lPad(myArray(0),"0",2) sData = myArray(1) If uBound(myArray) = 2 Then If myArray(2) = "true" Then bTime = true Else bTime = false End If Else bTime = false End If Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If If bTime = True Then If Len(sData) = 4 and Cint(sData) < 2400 Then iHour = mid(sData,1,2) iMin = mid(sData,3,2) iHour = hex(iHour) iMin = hex(iMin) sData = iHour & iMin sData = Cint("&H" & sData) sData = lPad(sData,"0",5) Else e "customvalue_write(): an invalid timestamp has been specified" Exit Sub End If Else sData = lPad(sData,"0",5) End If sString = "cw" & iValue & sData & "00" sString = generateString(sString) SendToElk sString End Sub Sub cs() ' request a status update of all 208 outputs, no parameters are requird ' usage : my.elk.txt("cs","") ' example: my.elk.txt("cs","") ' sString = "cs" sString = generateString(sString) SendToElk sString End Sub Sub zs() ' request a status update of all 208 zones, no parameters are requird ' usage : my.elk.txt("zs","") ' example: my.elk.txt("zs","") ' sString = "zs" sString = generateString(sString) SendToElk sString End Sub Sub plc_raw(strParams) ' control PLC device, supporting advanced parameters ' usage : my.elk.txt("plc_raw","sHouseCode|iUnitCode|iTimer|iCommand|iParameter") ' example: my.elk.txt("plc_raw","A|2|15|6|4") ' ' iTimer = ON Time in seconds, range - 0 to 9999 ' ' iCommand: ' 01 = X10_ALL_UNITS_OFF in a House code ' 02 = X10_ALL_LIGHTS_ON in a House code ' 03 = X10_UNIT_ON ' 04 = X10_UNIT_OFF ' 05 = X10_DIM, iParameter value holds number of dims ' 06 = X10_BRIGHT, iParameter value holds number of brights ' 07 = X10_ALL_LIGHTS_OFF in a House code ' 08 = X10_EXTENDED_CODE ' 09 = X10_PRESET_DIM, iParameter extended value hold level 0 to 99% ' 10 = X10_EXTENDED_DATA ' 11 = X10_STATUS_REQ ' 12 = X10_HAIL_REQUEST ' 13 = X10_HAIL_ACK, not used ' 14 = X10_STATUS_ON, not used ' 15 = X10_STATUS_OFF, not used ' If strParams <> "" Then myArray = split(strParams,"|") sHouseCode = myArray(0) iUnitCode = lPad(myArray(1),"0",2) iTimer = lPad(myArray(2),"0",4) iCommand = lPad(myArray(3),"0",2) iParameter = lPad(myArray(4),"0",2) Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "pc" & sHouseCode & iUnitCode & iCommand & iParameter & iTimer & "00" sString = generateString(sString) SendToElk sString End Sub Sub speak_word(strParams) ' speak word ' usage : my.elk.txt("speak_word","iWord") ' example: my.elk.txt("speak_word","123") ' If strParams <> "" Then iWord = lPad(strParams,"0",3) Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "sw" & iWord & "00" sString = generateString(sString) SendToElk sString End Sub Sub speak_phrase(strParams) ' speak phrase ' usage : my.elk.txt("speak_phrase","iPhrase") ' example: my.elk.txt("speak_phrase","123") ' If strParams <> "" Then iPhrase = lPad(strParams,"0",3) Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "sp" & iPhrase & "00" sString = generateString(sString) SendToElk sString End Sub Sub output_toggle(strParams) ' toggle output ' usage : my.elk.txt("output_toggle","iOutput") ' example: my.elk.txt("output_toggle","3") ' If strParams <> "" Then iOutput = lPad(strParams,"0",3) Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "ct" & iOutput & "00" sString = generateString(sString) SendToElk sString End Sub Sub output_off(strParams) ' turn output off ' usage : my.elk.txt("output_off","iOutput") ' example: my.elk.txt("output_off","3") ' If strParams <> "" Then iOutput = lPad(strParams,"0",3) Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "cf" & iOutput & "00" sString = generateString(sString) SendToElk sString End Sub Sub output_on(strParams) ' turn output on (For x seconds) ' usage : my.elk.txt("output_on","iOutput|iSeconds") ' example: my.elk.txt("output_on","3|4") ' If strParams <> "" Then myArray = split(strParams,"|") iOutput = lPad(myArray(0),"0",3) iSeconds = lPad(myArray(1),"0",5) Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "cn" & iOutput & iSeconds & "00" sString = generateString(sString) SendToElk sString End Sub Sub task_activate(strParams) ' activate task ' usage : my.elk.txt("task_activate","iTask") ' example: my.elk.txt("task_activate","21") ' If strParams <> "" Then iTask = strParams iTask = lPad(iTask,"0",3) Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "tn" & iTask & "00" sString = generateString(sString) SendToElk sString End Sub Sub voltage_request(strParams) ' request zone voltage ' usage : my.elk.txt("voltage_request","iZone") ' example: my.elk.txt("voltage_request","1") ' If strParams <> "" Then iZone = strParams iZone = lPad(iZone,"0",3) Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "zv" & iZone & "00" sString = generateString(sString) SendToElk sString End Sub Sub plc_toggle(strParams) ' toggle PLC device ' usage : my.elk.txt("plc_toggle","sHouseCode|sUnitCode") ' example: my.elk.txt("plc_toggle","A|2") ' If strParams <> "" Then myArray = split(strParams,"|") sHouseCode = myArray(0) sUnitCode = myArray(1) sUnitCode = lPad(sUnitCode,"0",2) Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "pt" & sHouseCode & sUnitCode & "00" sString = generateString(sString) SendToElk sString End Sub Sub plc_off(strParams) ' turn PLC device off ' usage : my.elk.txt("plc_off","sHouseCode|sUnitCode") ' example: my.elk.txt("plc_off","A|2") ' If strParams <> "" Then myArray = split(strParams,"|") sHouseCode = myArray(0) sUnitCode = myArray(1) If Len(sUnitCode) = 1 Then sUnitCode = "0" & sUnitCode Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "pf" & sHouseCode & sUnitCode & "00" sString = generateString(sString) SendToElk sString End Sub Sub plc_on(strParams) ' turn PLC device on ' usage : my.elk.txt("plc_on","sHouseCode|sUnitCode") ' example: my.elk.txt("plc_on","A|2") ' If strParams <> "" Then myArray = split(strParams,"|") sHouseCode = myArray(0) sUnitCode = lPad(myArray(1),"0",2) Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "pn" & sHouseCode & sUnitCode & "00" sString = generateString(sString) SendToElk sString End Sub Sub display_message(strParams) ' display message on keypad ' usage : my.elk.txt("display_message","iKeypadArea|iClear|iBeep|iTimeOut|sLine01|sLine02") ' example: my.elk.txt("display_message","1|2|1|15|Take out trash!") ' ' iKeypadArea: 1 - 8 ' iClear : 0 = clear message ' : 1 = clear message with & key ' : 2 = display until timeout ' iBeep : 0 = no beep ' : 1 = beep ' iTimeOut : 0 = no timeout ' : 1 - 65535 = display time in seconds ' sMessage : message to display, use ^ to start a new line If strParams <> "" Then myArray = split(strParams,"|") iKeypadArea = myArray(0) iClear = myArray(1) iBeep = myArray(2) iTimeOut = lPad(myArray(3),"0",5) sLine01 = myArray(4) If Len(sLine01) < 16 Then sLine01 = sLine01 & "^" If uBound(myArray) > 4 Then sLine02 = myArray(5) Else sLine02 = "^" End If Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "dm" & iKeypadArea & iClear & iBeep & iTimeOut & sLine01 & sLine02 & "00" sString = generateString(sString) SendToElk sString End Sub Sub thermostat_set(strParams) ' adjust thermostat ' usage : my.elk.txt("thermostat_set","iThermostat|iOption|iValue") ' example: my.elk.txt("thermostat_set","1|4|72") : Thermostat 1: Cool Set Point = 72 degrees ' ' iOption: ' 0 = Mode iValue = [00=Off,01=Heat,02=Cool,03=Auto, 04=Emergency Heat] ' 1 = Hold iValue = [Hold current temperature. 00=False, 01=True] ' 2 = Fan VV= [00=Fan Auto, 01=Fan turned on] ' 3 = not used ' 4 = CoolSetPoint iValue = [Cool setpoint If in Cool/auto mode, 01 to 99] ' 5 = HeatSetPoint iValue = [Heat setpoint If in heat/auto mode, 01 to 99] ' If strParams <> "" Then myArray = split(strParams,"|") iThermostat = lPad(myArray(0),"0",2) iOption = myArray(1) iValue = lPad(myArray(2),"0",2) Else e "1 or more parameters have not been specified, exiting ..." Exit Sub End If sString = "ts" & iThermostat & iValue & iOption & "00" sString = generateString(sString) SendToElk sString End Sub function generateString(sData) Dim sString, iLen, x, y, sTmp sString = sData iLen = hex(Len(sString)+2) If Len(iLen) = 1 Then iLen = "0" & iLen iCRC = 0 sTmp = iLen & sString y = Len(sTmp) For x = 1 to y iCRC = iCRC + Asc(mid(sTmp,x,1)) Next iCRC = not iCRC iCRC = iCRC + 1 iCRC = iCRC and 255 iCRC = hex(iCRC) generateString = iLen & sString & iCRC End Function Sub main() End Sub Sub d(sText) If bDebug = true Then hs.writelog "my.elk:debug", sText End If End Sub Sub e(sText) hs.writelog "my.elk:error", sText End Sub Sub l(sText) hs.writelog "my.elk", sText End Sub Sub SendToElk(sData) iTimerStart = Timer sData = sData & vbCrLf d "SendToElk(): " & sData If bSimulate <> True Then d "SendToElk(): bAutoOpen = True, serial port will be opened if required" If bAutoOpen = True Then OpenSerialPort hs.SendToComPort iSerialPort, sData Else d "iSimulate has been set to TRUE, no data has been sent to the serial port" End If iTimerStop = Timer iExecutionTime = iTimerStop - iTimerStart d "SendToElk() execution time: " & iExecutionTime & " seconds" End Sub Sub CloseSerialport() sError = hs.CloseComPort(iSerialPort) If sError <> "" Then e "CloseComPort(): " & sError Else d "COM" & iSerialPort & " has been closed" End If End Sub Sub OpenSerialPort() sVar1 = "my.elk.txt" sVar2 = "serial_processor" sError = hs.OpenComPort(iSerialPort, "115200,n,8,1", 1, sVar1,sVar2) If sError = "Port already open" Then If bAutoOpen <> True Then d "OpenSerialPort(): Port already open (COM" & iSerialPort & ")" End If Else If sError <> "" Then e "OpenSerialPort(): " & sError Else d "OpenSerialPort(): COM" & iSerialPort & " has been opened" If bAutoSync = True Then d "OpenSerialPort(): Synchronizing (bAutoSync=True)" hs.waitsecs(2) zs hs.waitsecs(2) cs Else d "OpenSerialPort(): No synchronizing will occur (bAutoSync=False)" End If End If End If End Sub Function lPad (sData, sChar, iLen) lPad = string(iLen - Len(sData), sChar) & sData End Function Function rPad (sData, sChar, iLen) rPad = sData & string(iLen - Len(sData), sChar) End Function ' ---------------------------------------------------------------- ' serial processor functions ' ---------------------------------------------------------------- Sub raw_processor (sData) sData = Trim(sData) sCommand = mid(sData,1,Instr(1,sData,":",1)-1) sParams = mid(sData,Instr(1,sData,":",1)+1) d "raw_processor(): sCommand = " & sCommand d "raw_processor(): sParams = " & sParams Select Case sCommand Case "speak" d "raw_processor(): speaking: " & sParams hs.speak sParams Case "event" d "raw_processor(): executing event " & sParams hs.TriggerEvent sParams Case "device" sDevice = mid(sParams,1,Instr(1,sParams,":",1)-1) sState = mid(sParams,Instr(1,sParams,":",1)+1) hs.ExecX10 sDevice, sState, 0, 0 Case Else d "raw_processor(): unknown RAW command received (sCommand = " & sCommand & ")" End Select End Sub function VerifyCRC(sLEN,sSTR,sCRC) iCRC = Cint("&H" & sCRC) sString = sLEN & sSTR For x = 1 to Len(sString) iCRC = iCRC + Asc(mid(sString,x,1)) Next iCRC = iCRC and 255 verifyCRC = iCRC End Function Sub zone_processor(iZone,sState) sDevice = hs.GetINISetting("my.elk.zones","zone" & iZone,"",sINIfile) If sDevice <> "" Then zone_update sState,sDevice Else e "zone " & iZone & ": zone has not been defined, ignoring update (state=" & sState & ")" End If End Sub Sub zvolt_processor(iZone,iVoltage) sDevice = hs.GetINISetting("my.elk.voltages","zone" & iZone,"",sINIfile) iVoltage = iVoltage / 10 If sDevice <> "" Then hs.SetDeviceString sDevice, iVoltage & "V", True d "zvolt_processor(): zone=" & iZone & " voltage=" & iVoltage Else e "zone " & iZone & ": zone has not been defined, ignoring update (Voltage=" & iVoltage & "V)" End If End Sub Sub output_processor(iOutput,iState) sDevice = hs.GetINISetting("my.elk.outputs","output" & iOutput,"",sINIfile) If sDevice <> "" Then If iState = 1 Then hs.ExecX10 sDevice, "ON" If iState = 0 Then hs.ExecX10 sDevice, "OFF" d "output_processor(): Output=" & iOutput & " State=" & iState Else e "output " & iOutput & ": output has not been defined, ignoring update (State=" & iState & ")" End If End Sub Sub keypadTemp_processor(iKeypad, iTemp) sDevice = hs.GetINISetting("my.elk.keypad.temp","keypad" & iKeypad,"",sINIfile) iTemp = iTemp - 40 d "keypadTemp_processor(): Keypad " & iKeypad & ": " & iTemp & "F" If sDevice <> "" Then d "keypadTemp_processor(): updating " & sDevice hs.SetDeviceString sDevice, iTemp & "F", True hs.SetDeviceValue sDevice, iTemp Else e "Keypad " & iKeypad & " has not been defined, ignoring update (Temperature=" & iTemp & "F)" End If End Sub Sub keypadKey_processor(iKeypad, iKey) sKeys = Array("11=STAR","12=POUND","13=F1","14=F2","15=F3","16=F4","17=STAY","18=EXIT","19=CHIME","20=BYPASS","21=ELK","22=DOWN","23=UP","24=RIGHT","25=LEFT","26=F6","27=F5","28=DATAKEYMODE") sKey = iKey for x = lBound(sKeys) to uBound(sKeys) If sKey = iKey Then sKey = Replace(iKey,mid(sKeys(x),1,2),mid(sKeys(x),4)) End If next d "keypadKey_processor(): Keypad " & iKeypad & ": " & sKey & " button has been pressed" sExecute = hs.GetINISetting("my.elk.keypad.keys","keypad" & iKeypad & ":" & sKey,"",sINIfile) If Instr(sExecute,"[event:") Then sExecute = Mid(sExecute,8,Len(sExecute) - 8) If hs.EventExists(sExecute) Then d "keypadKey_processor(): executing '" & sExecute & "' (" & sINIfile & ": " & "[my.elk.keypad.keys] => " & "keypad" & iKeypad & ":" & sKey & ")" hs.TriggerEvent sExecute Else e "keypadKey_processor(): event '" & sExecute & "' does not exist (" & sINIfile & ": " & "[my.elk.keypad.keys] => " & "keypad" & iKeypad & ":" & sKey & ")" End If Else e "keypadKey_processor(): Keypad " & iKeypad & ": " & sKey & " button has not been defined" End If End Sub Sub zonestatus_processor(sZones) d "zonestatus_processor(): received an update for all 208 zones (" & sZones & ")" myElkZones = hs.GetINISection("my.elk.zones",sINIfile) sTmp = Split(myElkZones,Chr(0)) For x = 0 to uBound(sTmp) iZone = Trim(Mid(sTmp(x),5,3)) sDevice = Trim(Mid(sTmp(x),9)) d "zonestatus_processor(): updating zone " & Cint(iZone) & " (" & sDevice & ")" zone_update Mid(sZones,iZone,1),sDevice Next End Sub Sub outputstatus_processor(sOutputs) d "outputstatus_processor(): received an update for all 208 outputs (" & sOutputs & ")" myElkOutputs = hs.GetINISection("my.elk.outputs",sINIfile) sTmp = Split(myElkOutputs,Chr(0)) For x = 0 to uBound(sTmp) iOutput = Trim(Mid(sTmp(x),7,3)) sDevice = Trim(Mid(sTmp(x),11)) d "outputstatus_processor(): updating output " & Cint(iOutput) & " (" & sDevice & ")" output_update Mid(sOutputs,iOutput,1),sDevice Next End Sub Sub output_update(sState,sDevice) If sState = 0 Then If hs.DeviceStatus(sDevice) <> 3 Then hs.ExecX10 sDevice, "OFF",0,0 Else If hs.DeviceStatus(sDevice) <> 2 Then hs.ExecX10 sDevice, "ON",0,0 End If End Sub Sub zone_update(sState,sDevice) Dim sDesc Dim iState Select Case sState Case "0" iState = 0 sDesc = "Normal: Unconfigured" Case "1" iState = 1 sDesc = "Normal: Open" Case "2" iState = 2 sDesc = "Normal: EOL" Case "3" iState = 3 sDesc = "Normal: Short" Case "5" iState = 5 sDesc = "Trouble: Open" Case "6" iState = 6 sDesc = "Trouble: EOL" Case "7" iState = 7 sDesc = "Trouble Short" Case "9" iState = 9 sDesc = "Violated: Open" Case "A" iState = 10 sDesc = "Violated: EOL" Case "B" iState = 11 sDesc = "Violated: Short" Case "D" iState = 12 sDesc = "Bypassed: Open" Case "E" iState = 13 sDesc = "Bypassed: EOL" Case "F" iState = 14 sDesc = "Bypassed: Short" End Select If sDevice <> "" Then If iState < 4 Then If hs.DeviceStatus(sDevice) = 2 Then hs.ExecX10 sDevice, "OFF",0,0 End If If iState = 9 or iState = 10 or iState = 11 Then If hs.DeviceStatus(sDevice) = 3 Then hs.ExecX10 sDevice, "ON",0,0 End If If sDesc = "" Then sDesc = "zone " & iZone & " " & iState & "update received, but is in an unknown state" e sDesc End If hs.SetDeviceString sDevice, sDesc End If End Sub Sub rp_processor(bState) If bState = True Then l "ElkRP connection has been detected, no data will be received until this connection has been closed" Else l "ElkRP has been disconnected, resuming data processing" End If End Sub