' Gambas class file ' Description: ' CKNX.class ' Provide support for KNX using eibd. ' Development Status: ' Working ' Links: ' http://www.auto.tuwien.ac.at/~mkoegler/index.php/eibd ' DomotiGa - an open source home automation program. ' Copyright (C) Ron Klinkien, The Netherlands. ' This module is written by Timo Sariwating in 2009. ' Updated by Ron Klinkien in 2014. ' Updated by Mark Parker - 15-12-2018 to fix status response on dimmer and shutter ' Updated by Mark Parker - 15-12-2018 to fix device address error ' Updated by Mark Parker - 19-12-2018 to multipe command and status issues ' Read file called COPYING for license details. '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' Module/Class specific variables '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Public PluginName As String = "KNX" Public PluginFriendlyName As String = "KNX" Public PluginVersion As String = "2.00" Public PluginAuthor As String = "Timo Sariwating, Ron Klinkien" Public PluginProtocols As String[] Public PluginMaxInstances As Integer = 1 Public KeyName As String Public LogLabel As String = "[KNX] " Public InterfaceId As Integer Public Instance As Integer Public IsRunning As Boolean Public ErrorText As String Public ErrorWhere As String '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' Private Variables '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private $bEnabled As Boolean Private $sTCPHost As String Private $iTCPPort As Integer Private $bDebug As Boolean ' Interface objects Public hMySocket As CSocket Public tKNX As Timer Private $aBuffer As New Byte[512] Private $iRead As Integer = 0 Private Const CmdOn As Byte = &H81 Private Const CmdOff As Byte = &H80 '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' Mandatory subroutine for each Module/Class to initialize: ' - The cPlugin[x].Settings are copied into local variables ' - Port/Connection will be started (any errors caught) ' - Any other code per Class '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Public Sub StartPlugin(cPl As CPluginEntry) KeyName = cPl.KeyName Instance = cPl.Instance If Instance <> 1 Then LogLabel = Replace(LogLabel, "] ", "#" & Instance & "] ") ' Copy configuration items locally $bEnabled = cPl.Settings["enabled"] $sTCPHost = cPl.Settings["tcphost"] $iTCPPort = cPl.Settings["tcpport"] $bDebug = cPl.Settings["debug"] InterfaceId = Devices.FindInterface("KNX Interface") If InterfaceId = 0 Then ErrorText = "Required InterfaceId can't be retrieved from the database!" WriteLog("ERROR: " & ErrorText) IsRunning = False Return Endif ' Connect/Initialize connection ConnectTCP() End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' Mandatory sub for each Module/Class to stop '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Public Sub StopPlugin() Try Disconnect() End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' Easy helper for WriteLog '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub WriteLog(sLog As String) Main.WriteLog(LogLabel & sLog) If $bDebug Then Main.WriteDebugLog(LogLabel & sLog) End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' Easy helper for WriteDebugLog '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub WriteDebugLog(sLog As String) If $bDebug Then Main.WriteDebugLog(LogLabel & sLog) End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' Connect to the IP interface/eibd daemon '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConnectTCP() Dim iReconnectRetry As Integer = 0 ' try to close the connection Try hMySocket.Close Try iReconnectRetry = hMySocket.ReconnectRetry ' get a new one - but also pass on our previous reconnect counter hMySocket = New CSocket(iReconnectRetry) As "MySocket" hMySocket.DataType = &HFF + gb.Byte hMySocket.Connect($sTCPHost, $iTCPPort) ' start timer for KNX status LED tKNX = New Timer As "tKNXLED" tKNX.Delay = 250 tKNX.Stop ' Write to main logfile we are trying to connect WriteLog("TCP interface connecting to " & $sTCPHost & ":" & $iTCPPort) Catch ' some errors WriteLog("ERROR: " & PluginFriendlyName & " TCP interface FAILED to connect to " & $sTCPHost & ":" & $iTCPPort) WriteLog("ERROR: " & Error.Text) IsRunning = False ErrorText = Error.Text ErrorWhere = Error.Where End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' disconnect from the host '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub Disconnect() ' try to close the connection Try hMySocket.Close ' try stop running timers StopTimers() WriteLog("TCP connection closed.") Finally IsRunning = False ErrorText = "" ErrorWhere = "" Catch WriteLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub StopTimers() Try tKNX.Stop End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' Run after succesfull connect '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub Run() ' Empty internal buffer $iRead = 0 OpenGroupSocket() End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' peer closed tcp socket '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Public Sub MySocket_Closed() WriteLog("ERROR: TCP socket closed by peer.") IsRunning = False ErrorText = "TCP socket closed by peer" StopTimers() End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' error while connected/connecting to tcp host '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Public Sub MySocket_Error(sMsg As String) WriteLog("ERROR: " & sMsg) IsRunning = False ErrorText = sMsg StopTimers() End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' host ip address found '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Public Sub MySocket_Found() Log.Plugin_DNS_Found(LogLabel, $sTCPHost) End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' tcp socket is connected '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Public Sub MySocket_Ready() WriteLog("TCP interface connected.") IsRunning = True Run() End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' Called when we should reconnect to the tcp host '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Public Sub MySocket_Reconnect() ConnectTCP() End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' received data from the tcp port. vVar is gb.String '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Public Sub MySocket_Read(vVar As Variant) Dim bData As Byte If vVar Then For Each bData In vVar ProcessReceivedChar(bData) Next Endif End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' open a group socket for group communication '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub OpenGroupSocket() Dim bCommand As Byte[] = [&H00, &H26, &H00, &H00, &H00] If $bDebug Then WriteDebugLog("Sending the command to open a Group Socket.") TransmitData(bCommand) End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' get list of KNX devices and query their status '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Public Sub GroupRead() Dim rResult As Result ' get a list of KNX devices rResult = Devices.FindDevicesForInterface(InterfaceId, Instance) If rResult And If rResult.Available Then For Each rResult If $bDebug Then WriteDebugLog("Sending Group Read request to device '" & rResult!name & "' with address '" & rResult!address & "'.") SendGroup(rResult!address) Next Else If $bDebug Then WriteDebugLog("No devices found to send Group Read request to.") Endif End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' blink led '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Public Sub tKNXLED_Timer() Main.ControlLed("KNX", "Off") tKNX.Stop End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' send a packet to the KNX interface '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub TransmitData(bCommand As Byte[]) Dim iLen As Integer = bCommand.Count If hMySocket.Status <> Net.Connected Then WriteDebugLog("ERROR: " & PluginFriendlyName & " Not connected to gateway: '" & Error.Text & "' at '" & Error.Where & "'!") Return Endif bCommand.Add(&H00, 0) bCommand.Add(Hex(iLen), 1) ' Write to socket hMySocket.Write(bCommand) If $bDebug Then WriteDebugLog("> " & Util.ByteToHex(bCommand)) Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' read KNX socket data '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessReceivedChar(bTemp As Byte) Dim iCnt As Integer Dim sDevice, sAddress As String Dim aReceived As New String[0] If hMySocket.Status <> Net.Connected Then WriteDebugLog("Not connected to " & PluginFriendlyName & " gateway: '" & Error.Text & "' at '" & Error.Where & "'") Return Endif ' Increment read byte counter Inc $iRead ' First byte should always be '00' If $iRead = 1 Then If bTemp <> 0 Then WriteDebugLog("ERROR: First byte is '" & Hex(bTemp, 2) & "', it is expected to be '00'") $iRead = 0 Return Endif $aBuffer[0] = bTemp Main.ControlLed("KNX", "On") If Main.bServer Then tKNX.Start Return Endif ' Second byte is the length of the data If $iRead = 2 Then $aBuffer[1] = bTemp Return Endif ' Now read data until we have all the data $aBuffer[$iRead - 1] = bTemp If ($iRead - 2) < $aBuffer[1] Then Return ' display raw data first If $bDebug Then WriteDebugLog("< " & Util.ByteToHex($aBuffer.Copy(0, $iRead))) ' We have read all the data, reset read counter to zero for next packet $iRead = 0 Select Hex$($aBuffer[3]) Case 26 ' open group socket reply If $bDebug Then WriteDebugLog("Received a Group Socket reply packet.") ' request status of all known devices GroupRead() Case 27 ' group packet sDevice = Addr2Str((Hex$($aBuffer[4], 2)) & (Hex$($aBuffer[5], 2)), False) sAddress = Addr2Str((Hex$($aBuffer[6], 2)) & (Hex$($aBuffer[7], 2)), True) If $bDebug Then WriteDebugLog("Received packet from physical device with address '" & sDevice & "' to '" & sAddress & "'") For iCnt = 9 To $aBuffer.Length - 1 aReceived.Add(Hex($aBuffer[iCnt], 2)) Next ' FIXME: not parsing packets from 0.0.0 also blocks scripts sending data to bus ' parse data for target addresses (group) ' don't parse messages we have sent If sDevice <> "0.0.0" Then ProcessReceivedPacket(sAddress, aReceived, $aBuffer) Endif ' parse data for source devices (physical) ' don't parse messages we have sent If sDevice <> "0.0.0" Then ProcessReceivedPacket(sDevice, aReceived, $aBuffer) Endif Case Else If $bDebug Then WriteDebugLog("Received unknown packet type '" & Hex$($aBuffer[3]) & "'") End Select Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' lookup the KNX devicetype and process received data '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessReceivedPacket(sAddress As String, aReceived As String[], aBuffer As String[]) Dim iDeviceId As Integer = Devices.FindRegExp(sAddress, InterfaceId, Instance, "KNX Device") Dim sDeviceTypeDesc As String Dim aDeviceTypeDesc As String[] Dim iCnt As Integer If iDeviceId Then sDeviceTypeDesc = Devices.FindDeviceTypeDescriptionForDeviceId(iDeviceId) aDeviceTypeDesc = Scan(sDeviceTypeDesc, "DPT *.*") If aDeviceTypeDesc.Count = 2 Then Select aDeviceTypeDesc[0] Case "1" ' DPT 1.* 1-Bit value, raw 0,1 -> Off,On ProcessDPT1(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) ' FIXME: return correct values for control part Case "2" ' DPT 2.* 1-Bit value controlled/forced, raw 0,1 -> Off,On ProcessDPT2(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) ' FIXME: return correct values and implement steps Case "3" ' DPT 3.* 4-Bit control, raw ProcessDPT3(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case "4" ' DPT 4.* 1-Byte character, raw A9 -> © (iso-8859-1), raw 24 -> E (ascii) ProcessDPT4(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case "5" ' DPT 5.* 1-Byte relative value, raw 00 -> 0% ProcessDPT5(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case "6" ' DPT 6.* 1-Byte signed value, raw 00 B4-> -76 ProcessDPT6(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case "7" ' DPT 7.* 2-Byte unsigned value, raw 12 12 -> 3084 ProcessDPT7(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case "8" ' DPT 8.* 2-Byte signed value, raw ProcessDPT8(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case "9" ' DPT 9.* 2-Byte floating point value, raw 0C 97 -> 23.5 C ProcessDPT9(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case "10" ' DPT 10.001 3-Byte time of day, raw 54 32 11 -> Tu 20:50:17 ProcessDPT10(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case "11" ' DPT 11.001 3-Byte date, raw 15 0A 0E -> 21 10 14 ProcessDPT11(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case "12" ' DPT 12.001 4-Byte unsigned value, raw 13 24 52 60 -> 219690044 ProcessDPT12(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) ' FIXME: doesn't work correctly yet Case "13" ' DPT 13.* 4-Byte signed value, raw 13 24 52 60 -> 219690044 ProcessDPT13(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) ' FIXME: doesn't work correctly yet Case "14" ' DPT 14.* 4-Byte 32-bit counter, raw ProcessDPT14(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case "16" ' DPT 16.* string, raw 13 24 52 60 -> #L: ProcessDPT16(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case "17" ' DPT 17.001 1-Byte scene number, raw ProcessDPT18(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case "18" ' DPT 18.001 1-Byte scene control, raw ProcessDPT18(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case "19" ' DPT 19.001 8-Byte datetime, raw ProcessDPT19(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case "232" ' DPT 232.600 3-Byte rgb, raw ProcessDPT232(iDeviceId, aReceived, aDeviceTypeDesc[0] & "." & aDeviceTypeDesc[1]) Case Else WriteDebugLog("The KNX devicetype used for device with address '" & sAddress & "' is not yet supported, please report.") WriteDebugLog("Data received: '", True) For iCnt = 0 To aBuffer[1] + 2 Main.WriteDebugLogChars(" " & Hex(aBuffer[iCnt], 2), True) Next Main.WriteDebugLogChars("\n", True) End Select Else If sDeviceTypeDesc = "KNX Device Dimmer" ProcessEIS2(iDeviceId, aReceived) Else If sDeviceTypeDesc = "KNX Device Blinds" ProcessEIS2(iDeviceId, aReceived) Else If sDeviceTypeDesc = "KNX Device Shutter" ProcessEIS2(iDeviceId, aReceived) Endif Endif End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' parse DPT type and return converted value '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Public Sub ProcessDPTs(aReceived As String[], sDPT As String) As String Dim aDPT As String[] Dim iCnt As Integer aDPT = Scan(sDPT, "*.*") If aDPT.Count = 2 Then Select aDPT[0] Case "1" ' DPT 1.* 1-Bit value, raw 00 -> Off Return ConvertDPT1(aReceived, sDPT) Case "2" ' DPT 1.* 1-Bit controlled/forced value, raw 00 -> Off Return ConvertDPT2(aReceived, sDPT) ' FIXME: return correct values and implement steps Case "3" ' DPT 3.* 4-Bit control, raw Return ConvertDPT3(aReceived, sDPT) Case "4" ' DPT 4.* 1-Byte character, raw A9 -> © (iso-8859-1), raw 24 -> E (ascii) Return ConvertDPT4(aReceived, sDPT) Case "5" ' DPT 5.* 1-Byte relative value, raw 00 -> 0%, or raw FF -> 255 Return ConvertDPT5(aReceived, sDPT) Case "6" ' DPT 6.* 1-Byte signed value, raw 00 B4-> -76 Return ConvertDPT6(aReceived, sDPT) Case "7" ' DPT 7.* 2-Byte unsigned value, raw 12 12 -> 3084 Return ConvertDPT7(aReceived, sDPT) ' FIXME: return correct values Case "8" ' DPT 8.* 2-Byte signed value, raw Return ConvertDPT8(aReceived, sDPT) Case "9" ' DPT 9.* 2-Byte floating point value, raw 0C 97 -> 23.5 C Return ConvertDPT9(aReceived, sDPT) Case "10" ' DPT 10.001 3-Byte time of day, raw 54 32 11 -> Tue 20:50:17 Return ConvertDPT10(aReceived, sDPT) Case "11" ' DPT 11.001 3-Byte date, raw 15 0A 0E -> 21-10-2014 Return ConvertDPT11(aReceived, sDPT) ' FIXME: return correct values Case "12" ' DPT 12.001 4-Byte unsigned counter value, raw 13 24 52 60 -> 219690044 Return ConvertDPT12(aReceived, sDPT) Case "13" ' DPT 13.* 4-Byte signed counter value, raw Return ConvertDPT13(aReceived, sDPT) ' FIXME: return correct values Case "14" ' DPT 14.* 4-Byte/32-bit float, raw Return ConvertDPT14(aReceived, sDPT) Case "16" ' DPT 16.* string, raw 13 24 52 60 -> #L: Return ConvertDPT16(aReceived, sDPT) Case "17" ' DPT 17.001 1-Byte scene number, raw Return ConvertDPT17(aReceived, sDPT) Case "18" ' DPT 18.001 1-Byte scene control, raw Return ConvertDPT18(aReceived, sDPT) Case "19" ' DPT 19.001 8-Byte datetime, raw Return ConvertDPT19(aReceived, sDPT) Case "232" ' DPT 232.600 3-Byte rgb, raw Return ConvertDPT232(aReceived, sDPT) Case Else WriteDebugLog("The DPT " & sDPT & " is not yet supported, please report.") WriteDebugLog("Data received: '", True) For iCnt = 0 To aReceived[1] + 2 Main.WriteDebugLogChars(" " & Hex(aReceived[iCnt], 2), True) Next Main.WriteDebugLogChars("\n", True) End Select Endif End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' encode value into DPT type format bytes '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Public Sub EncodeDPTs(fValue As Variant, sDPT As String) As Byte[] Dim aDPT As String[] aDPT = Scan(sDPT, "*.*") If aDPT.Count = 2 Then Select aDPT[0] Case "1" ' DPT 1.* 1-Bit value, raw 00 -> Off Return ConvertDPT1(fValue, sDPT) ' ' Case "2" ' DPT 1.* 1-Bit controlled/forced value, raw 00 -> Off ' Return ConvertDPT2(aReceived, sDPT) ' ' ' FIXME: return correct values and implement steps ' Case "3" ' DPT 3.* 4-Bit control, raw ' Return ConvertDPT3(aReceived, sDPT) ' ' Case "4" ' DPT 4.* 1-Byte character, raw A9 -> © (iso-8859-1), raw 24 -> E (ascii) ' Return ConvertDPT4(aReceived, sDPT) ' ' Case "5" ' DPT 5.* 1-Byte relative value, raw 00 -> 0%, or raw FF -> 255 ' Return ConvertDPT5(aReceived, sDPT) ' ' Case "6" ' DPT 6.* 1-Byte signed value, raw 00 B4-> -76 ' Return ConvertDPT6(aReceived, sDPT) ' ' Case "7" ' DPT 7.* 2-Byte unsigned value, raw 12 12 -> 3084 ' Return ConvertDPT7(aReceived, sDPT) ' ' ' FIXME: return correct values ' Case "8" ' DPT 8.* 2-Byte signed value, raw ' Return ConvertDPT8(aReceived, sDPT) Case "9" ' DPT 9.* 2-Byte floating point value, raw 0C 97 -> 23.5 C Return EncodeDPT9(fValue, sDPT) ' ' Case "10" ' DPT 10.001 3-Byte time of day, raw 54 32 11 -> Tue 20:50:17 ' Return ConvertDPT10(aReceived, sDPT) ' ' Case "11" ' DPT 11.001 3-Byte date, raw 15 0A 0E -> 21-10-2014 ' Return ConvertDPT11(aReceived, sDPT) ' ' ' FIXME: return correct values ' Case "12" ' DPT 12.001 4-Byte unsigned counter value, raw 13 24 52 60 -> 219690044 ' Return ConvertDPT12(aReceived, sDPT) ' ' Case "13" ' DPT 13.* 4-Byte signed counter value, raw ' Return ConvertDPT13(aReceived, sDPT) ' ' ' FIXME: return correct values ' Case "14" ' DPT 14.* 4-Byte/32-bit float, raw ' Return ConvertDPT14(aReceived, sDPT) ' ' Case "16" ' DPT 16.* string, raw 13 24 52 60 -> #L: ' Return ConvertDPT16(aReceived, sDPT) ' ' Case "17" ' DPT 17.001 1-Byte scene number, raw ' Return ConvertDPT17(aReceived, sDPT) ' ' Case "18" ' DPT 18.001 1-Byte scene control, raw ' Return ConvertDPT18(aReceived, sDPT) ' ' Case "19" ' DPT 19.001 8-Byte datetime, raw ' Return ConvertDPT19(aReceived, sDPT) ' ' Case "232" ' DPT 232.600 3-Byte rgb, raw ' Return ConvertDPT232(aReceived, sDPT) Case Else WriteDebugLog("Encoding DPT " & sDPT & " is not yet supported, please report.") End Select Endif End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' encode DPT 9.* value, 2 Byte '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub EncodeDPT9(sValue As Variant, sDPT As String) As Byte[] Dim bData As New Byte[2] Dim iExp As Integer Dim iMant, iSign, i As Integer Dim fValue As Float = Val(Replace(sValue, ",", ".")) Select Case sDPT Case "9.001" ' sign bit If (fValue < 0) Then iSign = &H8000 ' mant iMant = (fValue * 100) While (iMant > 2047) iMant = Abs(iMant / 2) Inc iExp Wend i = iSign Or Lsl(iExp, 11) Or (iMant And &H07FF) bData[0] = Lsr(i, 8) bData[1] = i And &HFF Return bData Case Else Return Null End Select End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 1.* values, 1-Bit '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT1(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim sValue As String Select Case aReceived[0] Case "40", "80", "41", "81" ' receive/parse sValue = ConvertDPT1(aReceived, sDPT) Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select ' update device Devices.ValueUpdate(iDeviceId, 1, sValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & sValue & "'") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 1 ' return cmd string for DPT 1.* values '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT1(aReceived As String[], sDPT As String) As String Dim sValue As String Dim bValue As Boolean = BTst(Val(aReceived[0]), 0) Select Case sDPT Case "1.001" sValue = IIf(bValue, "On", "Off") Case "1.002" sValue = IIf(bValue, "True", "False") Case "1.003" sValue = IIf(bValue, "Enable", "Disable") Case "1.004" sValue = IIf(bValue, "Ramp", "No Ramp") Case "1.005" sValue = IIf(bValue, "Alarm", "No Alarm") Case "1.006" sValue = IIf(bValue, "High", "Low") Case "1.007" sValue = IIf(bValue, "Increase", "Decrease") Case "1.008" sValue = IIf(bValue, "Down", "Up") Case "1.009" sValue = IIf(bValue, "Close", "Open") Case "1.010" sValue = IIf(bValue, "Start", "Stop") Case "1.011" sValue = IIf(bValue, "Active", "Inactive") Case "1.012" sValue = IIf(bValue, "Inverted", "Not Inverted") Case "1.013" sValue = IIf(bValue, "Cyclically", "Start/Stop") Case "1.014" sValue = IIf(bValue, "Calculated", "Fixed") Case "1.015" sValue = IIf(bValue, "Reset", "No Action") Case "1.016" sValue = IIf(bValue, "Ack", "No Action") Case "1.017" sValue = IIf(bValue, "Trigger1", "Trigger2") Case "1.018" sValue = IIf(bValue, "Occupied", "Not Occupied") Case "1.019" sValue = IIf(bValue, "Open", "Closed") Case "1.021" sValue = IIf(bValue, "And", "Or") Case "1.022" sValue = IIf(bValue, "Scene B", "Scene A") Case "1.023" sValue = IIf(bValue, "Move Up/Down + StepStop Mode", "Only Move Up/Down Mode") Case "1.100" sValue = IIf(bValue, "Heating", "Cooling") Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 1.* packet!") sValue = "?" End Select Return sValue Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 2.* values, 1-Bit '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT2(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim sValue As String Select Case aReceived[0] Case "40", "80", "41", "81" ' receive/parse sValue = ConvertDPT2(aReceived, sDPT) Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select ' update device Devices.ValueUpdate(iDeviceId, 1, sValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & sValue & "'") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT2(aReceived As String[], sDPT As String) As String Dim sValue, sControl As String Dim bValue As Boolean = BTst(Val(aReceived[0]), 0) Dim bControl As Boolean = BTst(Val(aReceived[0]), 1) ' control bit set? sControl = IIf(bControl, "Control", "No Control") ' value bit set? Select Case sDPT Case "2.001" sValue = IIf(bValue, "On", "Off") Case "2.002" sValue = IIf(bValue, "True", "False") Case "2.003" sValue = IIf(bValue, "Enable", "Disable") Case "2.004" sValue = IIf(bValue, "Ramp", "No Ramp") Case "2.005" sValue = IIf(bValue, "Alarm", "No Alarm") Case "2.006" sValue = IIf(bValue, "High", "Low") Case "2.007" sValue = IIf(bValue, "Increase", "Decrease") Case "2.008" sValue = IIf(bValue, "Down", "Up") Case "2.009" sValue = IIf(bValue, "Close", "Open") Case "2.010" sValue = IIf(bValue, "Start", "Stop") Case "2.011" sValue = IIf(bValue, "Active", "Inactive") Case "2.012" sValue = IIf(bValue, "Inverted", "Not Inverted") Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 2.* packet!") sValue = "?" End Select Return sValue Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 3.* values, 4-Bit control '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT3(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim sValue As String Select Case aReceived[0] Case "40", "80", "41", "81" ' receive/parse sValue = ConvertDPT3(aReceived, sDPT) Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select ' update device Devices.ValueUpdate(iDeviceId, 1, sValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & sValue & "'") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 3 ' 4 bit Dim (DPT 3.007) ' 4 bit Blind (DPT 3.008) ' RRRRDSSS ' R Reserved ' D Direction 0 = Decrease / 1 = Increase ' S Step (1-7) 0 = Break ' return cmd string for DPT 3.* values '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT3(aReceived As String[], sDPT As String) As String Dim sValue As String Dim bValue As Boolean = BTst(Val(aReceived[1]), 3) Dim iSteps As Integer = Val("&H" & aReceived[1]) And &H07 ' FIXME: return correct values and implement steps Select Case sDPT Case "3.007" ' dimming sValue = IIf(bValue = 1, "Brighten", "Dim") If iSteps = 0 Then sValue = "Stop" Else sValue &= " " & iSteps & " steps" Endif Case "3.008" ' blinds sValue = IIf(bValue = 1, "Down", "Up") If iSteps = 0 Then sValue = "Stop" Else sValue &= " " & iSteps & " steps" Endif Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 3.* packet!") sValue = "?" End Select Return sValue Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 4.* values, 1-Byte character '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT4(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim iValue As Float Select Case aReceived[0] Case "80", "40" ' receive/parse iValue = ConvertDPT5(aReceived, sDPT) Devices.ValueUpdate(iDeviceId, 1, iValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & iValue & "'") Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 4 ' convert from hex to character '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT4(aReceived As String[], sDPT As String) As String Dim sValue As String Select Case sDPT Case "4.001" ' character ASCII sValue = Chr(Val("&H" & aReceived[1])) Case "4.002" ' character ISO 8859-1 sValue = String.Chr(Val("&H" & aReceived[1])) Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 4.* packet!") sValue = "" End Select Return sValue Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 5.* values, 1-Byte relative '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT5(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim iValue As Float Select Case aReceived[0] Case "80", "40" ' receive/parse iValue = ConvertDPT5(aReceived, sDPT) Devices.ValueUpdate(iDeviceId, 1, iValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & iValue & "'") Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 5 ' convert from hex to int with 100, 255 or 360 max '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT5(aReceived As String[], sDPT As String) As Integer Dim iValue As Integer Select Case sDPT Case "5.xxx", "5.001" ' Relative Value (0..100%) iValue = Val("&H" & aReceived[1]) * 100 / 255 Case "5.003" ' Angle (0..360°) iValue = Val("&H" & aReceived[1]) * 360 / 255 Case "5.004" ' Decimal (0..255) iValue = Val("&H" & aReceived[1]) Case "5.005", "5.006", "5.010" ' Decimal (0..255) iValue = Val("&H" & aReceived[1]) Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 5.* packet!") iValue = 0 End Select Return iValue Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 6.* values, 1-Byte signed '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT6(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim iValue As Float Select Case aReceived[0] Case "80", "40" ' receive/parse iValue = ConvertDPT6(aReceived, sDPT) Devices.ValueUpdate(iDeviceId, 1, iValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & iValue & "'") Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 6 ' convert from hex to int with value -127 - 127 '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT6(aReceived As String[], sDPT As String) As Integer Dim iValue As Integer Select Case sDPT Case "6.001", "6.010" ' (-128..127) iValue = Val("&H" & aReceived[1]) Case "6.020" ' status with mode FIXME: unsure if correct iValue = Val("&H" & aReceived[1]) Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 6.* packet!") iValue = 0 End Select If iValue > 128 Then iValue -= 256 Return iValue Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 7.* values, 2-Byte unsigned '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT7(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim iValue As Float Select Case aReceived[0] Case "80", "40" ' receive/parse iValue = ConvertDPT7(aReceived, sDPT) Devices.ValueUpdate(iDeviceId, 1, iValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & iValue & "'") Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 7 ' convert from hex to int 0..65535 '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT7(aReceived As String[], sDPT As String) As Integer Dim iValue As Integer Select Case sDPT Case "7.xxx", "7.001", "7.002", "7.005", "7.006", "7.007", "7.010", "7.011", "7.012", "7.013" ' unsigned (0..65535) iValue = Lsl(Val("&H" & aReceived[1]), 8) Or Val("&H" & aReceived[2]) Case "7.003" ' 10 mS signed (0..655350) iValue = 10 * (Lsl(Val("&H" & aReceived[1]), 8) Or Val("&H" & aReceived[2])) Case "7.004" ' 100 mS signed (0..6553500) iValue = 100 * (Lsl(Val("&H" & aReceived[1]), 8) Or Val("&H" & aReceived[2])) Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 7.* packet!") iValue = 0 End Select Return iValue Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 8.* values, 2-Byte signed '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT8(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim iValue As Float Select Case aReceived[0] Case "80", "40" ' receive/parse iValue = ConvertDPT8(aReceived, sDPT) Devices.ValueUpdate(iDeviceId, 1, iValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & iValue & "'") Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 8 ' convert from hex to int -32768..32767 '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT8(aReceived As String[], sDPT As String) As Integer Dim iValue As Integer Select Case sDPT Case "8.xxx", "8.001", "8.002", "8.005", "8.006", "8.007", "8.010" ' signed (-32768..32767) ' iValue = Lsl(Val(aReceived[1]), 8) Or Val(aReceived[2]) iValue = Lsl(Val(aReceived[1]), 8) Or Val(aReceived[2]) Case "8.003" ' 10 mS signed (-327680..327670) iValue = 10 * (Lsl(Val(aReceived[1]), 8) Or Val(aReceived[2])) Case "8.004" ' 100 mS signed (-3276800..3276700) iValue = 100 * (Lsl(Val(aReceived[1]), 8) Or Val(aReceived[2])) Case "8.010" ' % iValue = 100 * (Lsl(Val(aReceived[1]), 8) Or Val(aReceived[2])) / 100 Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 8.* packet!") iValue = 0 End Select If iValue > 32768 Then iValue -= 65535 Return iValue Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 9.* values, 2-Byte floating point '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT9(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim fValue As Float Select Case aReceived[0] Case "80", "40" ' receive/parse fValue = ConvertDPT9(aReceived, sDPT) Devices.ValueUpdate(iDeviceId, 1, Format(fValue, "0.00")) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & fValue & "'") Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 9 ' 2 Byte Float E Exponent(0..15) ' SEEEEMMM MMMMMMMM M Mantisse (-2048...2047) ' S Sign (0/1) 7FFFH means invalid data '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT9(aReceived As String[], sDPT As String) As Float Dim iExp, iMant As Integer Dim iVal As Integer = Val("&H" & aReceived[1] & aReceived[2]) Select Case sDPT Case "9.001", "9.002", "9.003", "9.004", "9.005", "9.006", "9.007", "9.008", "9.009", "9.010", "9.011", "9.020", "9.021", "9.022", "9.023", "9.024", "9.025", "9.026", "9.027", "9.028" iMant = iVal And &H07FF If (iVal And &H08000&) Then iMant = iMant Or &HFFFFF800& iMant = - iMant Endif iExp = Lsr((iVal And &H07800&), 11) If (iVal And &H08000&) Then iMant = - iMant Return (iMant * Lsl(1, iExp) / 100) Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 9.* packet!") End Select Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data or send for DPT 10.001 values, 3-Byte time of day ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT10(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim sValue As String Dim aAddress As String[] Dim bCommand As New Byte[9] Select Case aReceived[0] Case "80" ' receive/parse sValue = ConvertDPT10(aReceived, sDPT) Devices.ValueUpdate(iDeviceId, 1, sValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & sValue & "'") Case "00" ' requested response aAddress = Split(Devices.FindAddressForDevice(iDeviceId), "/") bCommand[0] = &H00 bCommand[1] = &H27 bCommand[2] = Hex$(Val(aAddress[0]) & Val(aAddress[1])) bCommand[3] = Val(aAddress[2]) bCommand[4] = &H00 bCommand[5] = &H80 bCommand[6] = Hex$(Hour(Now)) bCommand[7] = Hex$(Minute(Now)) bCommand[8] = Hex$(Second(Now)) TransmitData(bCommand) If $bDebug Then WriteDebugLog(" DPT 10.001 time '" & Time(Now) & "' send to the bus.") End Select End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 10 ' 3 Byte Time ' DDDHHHHH RRMMMMMM RRSSSSSS H Hour ' R Reserved M Minutes ' D WeekDay S Seconds '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT10(aReceived As String[], sDPT As String) As String Dim aWeekDays As String[] = ["", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] Dim iWeekDay, iHour, iMin, iSec As Integer Dim sValue As String Select Case sDPT Case "10.001" iWeekday = Lsr(Val("&H" & aReceived[1]), 5) iHour = Val("&H" & aReceived[1]) And &H1F iMin = Val("&H" & aReceived[2]) iSec = Val("&H" & aReceived[3]) If Len(aWeekdays[iWeekday]) Then sValue = aWeekdays[iWeekday] & " " Return sValue & Format(iHour, "00") & ":" & Format(iMin, "00") & ":" & Format(iSec, "00") Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 10.* packet!") Return "" End Select Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data or send for DPT 11.001 values, 3-Byte date ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT11(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim sValue As String Dim aAddress As String[] Dim bCommand As New Byte[9] Select Case aReceived[0] Case "80" ' receive/parse sValue = ConvertDPT11(aReceived, sDPT) Devices.ValueUpdate(iDeviceId, 1, sValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & sValue & "'") Case "00" ' requested response aAddress = Split(Devices.FindAddressForDevice(iDeviceId), "/") bCommand[0] = &H00 bCommand[1] = &H27 bCommand[2] = Hex$(Val(aAddress[0]) & Val(aAddress[1])) bCommand[3] = Val(aAddress[2]) bCommand[4] = &H00 bCommand[5] = &H80 bCommand[6] = Hex$(Day(Now)) bCommand[7] = Hex$(Month(Now)) bCommand[8] = Hex$(Right$(CStr(Year(Now)), 2)) TransmitData(bCommand) If $bDebug Then WriteDebugLog(" DPT 11.001 date '" & Day(Now) & "/" & Month(Now) & "/" & Year(Now) & "' send to the bus.") End Select End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 11 ' 3 byte Date ' RRRDDDDD RRRRMMMM RYYYYYYY M Month ' R Reserved Y Year '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT11(aReceived As String[], sDPT As String) As String Dim iDay, iMonth, iYear As Integer Select Case sDPT Case "11.001" iDay = Val("&H" & aReceived[1]) iMonth = Val("&H" & aReceived[2]) iYear = Val("&H" & aReceived[3]) If iYear > 90 Then iYear += 1900 If iYear < 90 Then iYear += 2000 Return Format(iYear, "####") & "-" & Format(iMonth, "00") & "-" & Format(iDay, "00") Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 11.* packet!") Return "" End Select Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 12.001 values, 4-Byte unsigned '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT12(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim iValue As Integer Select Case aReceived[0] Case "80", "40" ' receive/parse iValue = ConvertDPT12(aReceived, sDPT) Devices.ValueUpdate(iDeviceId, 1, iValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & iValue & "'") Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 12 ' convert from hex to 4 byte unsigned '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT12(aReceived As String[], sDPT As String) As Long Dim lValue As Long Select Case sDPT Case "12.001", lValue = Val("&H" & aReceived[1] & aReceived[2] & aReceived[3] & aReceived[4] & "&") Return lValue Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 12.* packet!") Return 0 End Select Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 13.* values, 4-Byte signed '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT13(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim iValue As Integer Select Case aReceived[0] Case "80", "40" ' receive/parse iValue = ConvertDPT13(aReceived, sDPT) Devices.ValueUpdate(iDeviceId, 1, iValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & iValue & "'") Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 13 ' convert from hex to 4 byte signed '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT13(aReceived As String[], sDPT As String) As Long Dim lValue As Long Select Case sDPT Case "13.001", "13.002", "13.010", "13.011", "13.012", "13.013", "13.014", "13.015", "13.100" ' signed (-2147483648..2147483647) lValue = Val("&H" & aReceived[1] & aReceived[2] & aReceived[3] & aReceived[4]) Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 13.* packet!") lValue = 0 End Select If lValue > 2147483647 Then lValue -= 2147483648 Return lValue Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 14.* values, 4-Byte float ' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT14(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim sValue As String Select Case aReceived[0] Case "80", "40" ' receive/parse sValue = Format(ConvertDPT14(aReceived, sDPT), "#.###") Devices.ValueUpdate(iDeviceId, 1, sValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & sValue & "'") Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 14 ' 4 Byte Float ' SEEEEEEE EFFFFFFF FFFFFFFF FFFFFFFF ' S(Sign) = {0,1} ' Exponent = [0...255] ' Fraction = [0..8 388 607] '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT14(aReceived As String[], sDPT As String) As Float Dim sHex As String = aReceived[1] & aReceived[2] & aReceived[3] & aReceived[4] Dim iSign As Integer Dim fExponent, fMant As Float ' Sub decode_dpt14 { #4byte float ' #Perls unpack For float Is Somehow strange broken ' my@Val = Split(" ", shift); ' my $val2 = (Hex($val[0]) < < 24) + (Hex($val[1]) < < 16) + (Hex($val[2]) < < 8) + Hex($val[3]); ' my $sign = ($val2 & 0 x80000000) Print -1: 1; ' my $expo = (($val2 & 0 x7F800000) > > 23) - 127; ' my $mant = ($val2 & 0 x007FFFFF | 0x00800000); ' my $num = $sign * (2 * * $expo) * ($mant / (1 < < 23)); ' Return sprintf("%.4f", $num); ' } Select Case sDPT Case "14.000", "14.001" ' sign iSign = IIf((Val("&H" & Mid(sHex, 1, 2)) And &H80) = 128, -1, 1) ' exponent fExponent = (Val("&H" & Mid(sHex, 1, 3)) And &H7F8) / 2 ^ 3 - 127 ' mantissa fMant = (Val("&H" & Mid(sHex, 3, 6)) And &H7FFFFF) / 2 ^ 23 + 1 Return iSign * fMant * 2 ^ fExponent Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 14.* packet!") Return 0 End Select Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 16.* values, strings '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT16(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim sValue As String Select Case aReceived[0] Case "80", "40" ' receive/parse sValue = ConvertDPT16(aReceived, sDPT) Devices.ValueUpdate(iDeviceId, 1, sValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & sValue & "'") Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 16 ' convert from hex to character '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT16(aReceived As String[], sDPT As String) As String Dim sValue As String Dim iValue, iCnt As Integer Select Case sDPT Case "16.000" ' character ASCII For iCnt = 1 To aReceived.Count - 1 If iCnt = 15 Then Break ' Max 14 chars iValue = Val("&H" & aReceived[iCnt]) If iValue = 0 Then Break sValue &= Chr(iValue) Next Case "16.001" ' character ISO 8859-1 For iCnt = 1 To aReceived.Count - 1 If iCnt = 15 Then Break ' Max 14 chars iValue = Val("&H" & aReceived[iCnt]) If iValue = 0 Then Break sValue &= String.Chr(iValue) Next Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 16.* packet!") sValue = "" End Select Return sValue Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 17.001 1-Byte scene number '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT17(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim iValue As Integer Select Case aReceived[0] Case "40", "80", "41", "81" ' receive/parse iValue = ConvertDPT17(aReceived, sDPT) Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select ' update device Devices.ValueUpdate(iDeviceId, 1, Str(iValue)) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & Str(iValue) & "'") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 17 ' return cmd strings for DPT 17.* values '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT17(aReceived As String[], sDPT As String) As Integer Dim iScene As Integer Select Case sDPT Case "17.001" ' scene iScene = Val("&H" & aReceived[1]) And &H3F Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 17.* packet!") iScene = 0 End Select Return iScene Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 18.001 1-Byte scene control '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT18(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim sValue As String Select Case aReceived[0] Case "40", "80", "41", "81" ' receive/parse sValue = ConvertDPT18(aReceived, sDPT) Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select ' update device Devices.ValueUpdate(iDeviceId, 1, sValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & sValue & "'") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 18 ' return cmd strings for DPT 18.* values '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT18(aReceived As String[], sDPT As String) As String Dim sValue As String Dim iControl As Integer = Val("&H" & aReceived[1]) And &H80 Dim iScene As Integer = Val("&H" & aReceived[1]) And &H3F ' scene 0 - 63 Select Case sDPT Case "18.001" ' scene If iControl = 128 Then sValue = "learn" Else sValue = "activate" Endif sValue &= " " & iScene Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 18.* packet!") sValue = "?" End Select Return sValue Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 19.001 8-Byte datetime '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT19(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim sValue As String Select Case aReceived[0] Case "40", "80", "41", "81" ' receive/parse sValue = ConvertDPT19(aReceived, sDPT) Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select ' update device Devices.ValueUpdate(iDeviceId, 1, sValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & sValue & "'") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 19 ' return cmd strings for DPT 19.* datetime '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT19(aReceived As String[], sDPT As String) As String Dim aWeekDays As String[] = ["Any day", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] Dim sValue As String Dim bExtended As Boolean = True ' by default output extended format ' bit masks Dim DST As Byte = &H01 Dim NO_TIME As Byte = &H02 Dim NO_DOW As Byte = &H04 Dim NO_DATE As Byte = &H08 Dim NO_YEAR As Byte = &H10 Dim NO_WD As Byte = &H20 Dim WD As Byte = &H40 Dim FAULT As Byte = &H80 ' extended field bit masks Dim QUALITY As Byte = &H80 Select Case sDPT Case "19.001" ' datetime ' time faulty If (Val("&H" & aReceived[7]) And FAULT) Then Return "Corrupted date/time!" ' year If Not (Val("&H" & aReceived[7]) And NO_YEAR) Then sValue = Val("&H" & aReceived[1]) + 1900 Endif ' month, day If Not (Val("&H" & aReceived[7]) And NO_DATE) Then sValue &= "-" & Format(Val("&H" & aReceived[2]), "0#") & "-" & Format(Val("&H" & aReceived[3]), "0#") Endif If bExtended Then ' day of week If Not (Val("&H" & aReceived[7]) And NO_DOW) Then sValue &= " " & aWeekDays[Lsr(Val("&H" & aReceived[4]), 5)] Endif ' workday yes/no If Not (Val("&H" & aReceived[7]) And NO_WD) Then sValue &= " (" & IIf(Val("&H" & aReceived[7]) And WD, "workday", "no workday") & ")" Endif Endif ' time hr:min:sec If Not (Val("&H" & aReceived[7]) And NO_TIME) Then sValue &= " " & Format(Val("&H" & aReceived[4]) And &H1F, "0#") sValue &= ":" & Format(Val("&H" & aReceived[5]), "0#") sValue &= ":" & Format(Val("&H" & aReceived[6]), "0#") If bExtended And (Val("&H" & aReceived[7]) And DST) Then sValue &= " DST" Endif Endif ' clock quality If bExtended Then sValue &= ", " & IIf(Val("&H" & aReceived[8]) And QUALITY, "in sync", "no sync") Endif Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 19.* packet!") sValue = "?" End Select Return sValue Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' process received data for DPT 232.* values, RGB '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessDPT232(iDeviceId As Integer, aReceived As String[], sDPT As String) Dim sValue As String Select Case aReceived[0] Case "40", "80", "41", "81" ' receive/parse sValue = ConvertDPT232(aReceived, sDPT) Case Else If $bDebug Then WriteDebugLog("Unknown data '" & aReceived[0] & "' received for DPT " & sDPT & " packet!") Return End Select ' update device Devices.ValueUpdate(iDeviceId, 1, sValue) If $bDebug Then WriteDebugLog("Processed received DPT " & sDPT & " data for device '" & Devices.FindNameForDevice(iDeviceId) & "' value = '" & sValue & "'") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' DPT 232.600 ' return cmd string for DPT 232.* values 0-255,0-255,0-255 '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ConvertDPT232(aReceived As String[], sDPT As String) As String Dim sValue As String Dim iRed As Integer = Val("&H" & aReceived[1]) Dim iGreen As Integer = Val("&H" & aReceived[2]) Dim iBlue As Integer = Val("&H" & aReceived[3]) Select Case sDPT Case "232.600" ' rgb sValue = "r:" & iRed & " g:" & iGreen & " b:" & iBlue Case Else If $bDebug Then WriteDebugLog("Unknown DPT '" & sDPT & "' received for DPT 232.* packet!") sValue = "?" End Select Return sValue Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' handle received commands for EIS2 devices (dim) ' address format is 0/1/2|0/1/3|0/1/4 ' first address is used for on/off, second start/stop, third dim value '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub ProcessEIS2(iDeviceId As Integer, sReceived As String[]) Dim iPercentage As Integer Select Case sReceived[0] Case "40" Devices.ValueUpdate(iDeviceId, 1, "Off") iPercentage = Round(Val("&H" & sReceived[1]) / 255 * 100) If iPercentage = 0 Then Devices.ValueUpdate(iDeviceId, 1, "Off") Else If iPercentage = 100 Then Devices.ValueUpdate(iDeviceId, 1, "On") Else Devices.ValueUpdate(iDeviceId, 1, "Dim " & CStr(iPercentage)) Endif Case "41" If $bDebug Then WriteDebugLog("EIS2 packet for device '" & Devices.FindNameForDevice(iDeviceId) & "' Value = 'On'") Devices.ValueUpdate(iDeviceId, 1, "On") iPercentage = Round(Val("&H" & sReceived[1]) / 255 * 100) If iPercentage <> "0" And iPercentage <> "100" Then If $bDebug Then WriteDebugLog("EIS2 packet for device '" & Devices.FindNameForDevice(iDeviceId) & "' Value = 'Dim " & CStr(iPercentage) & "%'") Devices.ValueUpdate(iDeviceId, 1, "Dim " & CStr(iPercentage)) Endif Case "80" If sReceived[1] <> "00" Then iPercentage = Round(Val("&H" & sReceived[1]) / 255 * 100) If iPercentage = 0 Then Devices.ValueUpdate(iDeviceId, 1, "Off") Else If iPercentage = 100 Then Devices.ValueUpdate(iDeviceId, 1, "On") Else Devices.ValueUpdate(iDeviceId, 1, "Dim " & CStr(iPercentage)) Endif If $bDebug Then WriteDebugLog("EIS2 packet for device '" & Devices.FindNameForDevice(iDeviceId) & "' Value = 'Dim " & CStr(iPercentage) & "%'") Else If $bDebug Then WriteDebugLog("EIS2 packet for device '" & Devices.FindNameForDevice(iDeviceId) & "' Value = 'Off'") Devices.ValueUpdate(iDeviceId, 1, "Off") Endif Case "81" If $bDebug Then WriteDebugLog("EIS2 packet for device '" & Devices.FindNameForDevice(iDeviceId) & "' Value = 'On'") Devices.ValueUpdate(iDeviceId, 1, "On") Case "89" If $bDebug Then WriteDebugLog("EIS2 packet for device '" & Devices.FindNameForDevice(iDeviceId) & "' Value = 'Dim'") Case Else If $bDebug Then WriteDebugLog("Unknown value '" & sReceived[0] & "' received for EIS2 type data packet!") Return End Select End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' prepare a command for sending status request to KNX interface '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub SendGroup(sAddress As String) Dim sAddressSplit As String[] Dim sAddr As String Dim bCommand As New Byte[5] If $bDebug Then WriteDebugLog("Requesting status packet for device with address '" & sAddress & "'") sAddressSplit = Split(sAddress, "/") If sAddressSplit.Count = 3 Then sAddr = Str2Addr(sAddressSplit[0], sAddressSplit[1], sAddressSplit[2]) bCommand[0] = &H00 bCommand[1] = &H27 bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand[4] = &H00 bCommand.Add(&H00) TransmitData(bCommand) Else sAddressSplit = Split(sAddress, "|" "/") If sAddressSplit.Count = 9 Then sAddr = Str2Addr(sAddressSplit[3], sAddressSplit[4], sAddressSplit[5]) bCommand[0] = &H00 bCommand[1] = &H27 bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand[4] = &H00 bCommand.Add(&H00) TransmitData(bCommand) Endif Endif End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' extract address from given string and prepare for sending the KNX interface '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Private Sub CreateControlAddress(sAddress As String, Optional iPos As Integer = 1) As String Dim aAddress As String[] ' split group addresses and extract to convert the one we need aAddress = Split(sAddress, "|" "/") If aAddress.Count >= 3 * iPos Then Select Case iPos Case 1 Return Str2Addr(aAddress[0], aAddress[1], aAddress[2]) Case 2 Return Str2Addr(aAddress[3], aAddress[4], aAddress[5]) Case 3 Return Str2Addr(aAddress[6], aAddress[7], aAddress[8]) Case Else WriteDebugLog("Invalid address format used for '" & sAddress & "', should be in format '0/1/2' or '0/1/2|0/1/3' or '0/1/2|0/1/3|0/1/4'") End Select Else WriteDebugLog("Invalid address format used for '" & sAddress & "', should be in format '0/1/2' or '0/1/2|0/1/3' or '0/1/2|0/1/3|0/1/4'") Endif End '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ' prepare a command for sending to the KNX bus '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Public Sub SendCommand(sAddress As String, sValue As String) Dim sAddr, sDeviceTypeDesc As String Dim aDeviceTypeDesc As String[] Dim iBrightness As Integer Dim bCommand As New Byte[5] Dim bData As New Byte[] sValue = UCase(sValue) sDeviceTypeDesc = Devices.FindDeviceTypeDescriptionForDeviceId(Devices.Find(Instance, sAddress, InterfaceId)) aDeviceTypeDesc = Scan(sDeviceTypeDesc, "DPT *.*") If aDeviceTypeDesc.Count = 2 Then Select aDeviceTypeDesc[0] Case "1" ' DPT 1.* 1-Bit ' 0/1/2[|0/1/3|...] control and feedback address(es) ' extract first address and convert it to knx format sAddr = CreateControlAddress(sAddress) ' check if we have 2 bytes If Len(sAddr) <> 4 Then Return bCommand[0] = &H00 bCommand[1] = &H27 bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand[4] = &H00 Select Case sValue Case "OFF", "DISABLE", "NO ALARM", "LOW", "DECREASE", "UP", "STOP", "INACTIVE", "TRIGGER 0", "SCENE A", "COOLING" bCommand.Add(CmdOff) TransmitData(bCommand) Case "ON", "ENABLE", "ALARM", "HIGH", "INCREASE", "DOWN", "START", "ACTIVE", "TRIGGER 1", "SCENE B", "HEATING" bCommand.Add(CmdOn) TransmitData(bCommand) End Select Case "9" ' DPT 9.* 2 Byte Float E Exponent(0..15) ' 0/1/2[|0/1/3|...] control and feedback address(es) ' extract first address and convert it to knx format sAddr = CreateControlAddress(sAddress) ' check if we have 2 bytes If Len(sAddr) <> 4 Then Return bCommand[0] = &H00 bCommand[1] = &H27 bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand[4] = &H00 bData = EncodeDPT9(sValue, Right(sDeviceTypeDesc, Len(sDeviceTypeDesc) - 4)) bCommand.Add(bData[0]) bCommand.Add(bData[1]) TransmitData(bCommand) Case Else WriteDebugLog("Control of " & sDeviceTypeDesc & " type not implemented yet, please report.") End Select Else If UCase(sDeviceTypeDesc) = "KNX DEVICE DIMMER" ' KNX Device Actuator Basic is using three different object for control (command/control/value) ' Command is for switching the light on/off (DPT 1.001 1-Bit) ' Control is for start/stop dimming and brightening (4 Bit) ' Value is for setting a specific brightness level(0 - 100) 1-Byte ' If you have a dimmer which doesn't support start/stop specify -/-/- as middle address bCommand[0] = &H00 bCommand[1] = &H27 bCommand[4] = &H00 Select Case sValue Case "ON" sAddr = CreateControlAddress(sAddress) If Len(sAddr) <> 4 Then Return bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand.Add(CmdOn) TransmitData(bCommand) Case "OFF" sAddr = CreateControlAddress(sAddress) If Len(sAddr) <> 4 Then Return bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand.Add(CmdOff) TransmitData(bCommand) Case "STOP" If InStr(sAddress, "-/-/-") Then If $bDebug Then WriteDebugLog("Dimmer dummy '-/-/-' startstop address specified, skipping command.") Return Endif ' extract second address and convert it to knx format sAddr = CreateControlAddress(sAddress, 2) If Len(sAddr) <> 4 Then Return bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand.Add(&H80) TransmitData(bCommand) Case Else If InStr(sValue, "DIM ") Then ' DIM 0-100 iBrightness = Val(Replace(sValue, "DIM ", "")) ' extract third address and convert it to knx format sAddr = CreateControlAddress(sAddress, 3) If Len(sAddr) <> 4 Then Return bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand.Add(&H80) bCommand.Add(CInt(iBrightness * 2.55)) TransmitData(bCommand) Endif End Select Else If UCase(sDeviceTypeDesc) = "KNX DEVICE SHUTTER" ' KNX Device Actuator Basic is using three different object for control (command/control/value) ' Command is for movement of shutter up/down (DPT 1.008 1-Bit) ' Control is for start/stop movement (1 Bit) ' Value is for setting a specific position level(0 - 100%) 1-Byte DPT 5.001 ' If you have a shutter which doesn't support start/stop specify -/-/- as middle address bCommand[0] = &H00 bCommand[1] = &H27 bCommand[4] = &H00 Select Case sValue Case "ON", "UP" sAddr = CreateControlAddress(sAddress) If Len(sAddr) <> 4 Then Return bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand.Add(CmdOn) TransmitData(bCommand) Case "OFF", "DOWN" sAddr = CreateControlAddress(sAddress) If Len(sAddr) <> 4 Then Return bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand.Add(CmdOff) TransmitData(bCommand) Case "STOP" If InStr(sAddress, "-/-/-") Then If $bDebug Then WriteDebugLog("Shutter dummy '-/-/-' startstop address specified, skipping command.") Return Endif ' extract second address and convert it to knx format sAddr = CreateControlAddress(sAddress, 2) If Len(sAddr) <> 4 Then Return bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand.Add(&H80) TransmitData(bCommand) Case Else If InStr(sValue, "DIM ") Then ' DIM 0-100 iBrightness = Val(Replace(sValue, "DIM ", "")) ' extract third address and convert it to knx format sAddr = CreateControlAddress(sAddress, 3) If Len(sAddr) <> 4 Then Return bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand.Add(&H80) bCommand.Add(CInt(iBrightness * 2.55)) TransmitData(bCommand) Endif End Select Else If UCase(sDeviceTypeDesc) = "KNX DEVICE BLINDS" ' KNX Device Actuator Basic is using three different object for control (command/control/value) ' Command is for movement of shutter up/down (DPT 1.008 1-Bit) ' Control is for start/stop movement (1 Bit) ' Value is for setting a specific position level(0 - 100%) 1-Byte DPT 5.001 ' If you have a blind which doesn't support start/stop specify -/-/- as middle address bCommand[0] = &H00 bCommand[1] = &H27 bCommand[4] = &H00 Select Case sValue Case "ON", "UP" sAddr = CreateControlAddress(sAddress) If Len(sAddr) <> 4 Then Return bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand.Add(CmdOn) TransmitData(bCommand) Case "OFF", "DOWN" sAddr = CreateControlAddress(sAddress) If Len(sAddr) <> 4 Then Return bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand.Add(CmdOff) TransmitData(bCommand) Case "STOP" If InStr(sAddress, "-/-/-") Then If $bDebug Then WriteDebugLog("Blind dummy '-/-/-' startstop address specified, skipping command.") Return Endif ' extract second address and convert it to knx format sAddr = CreateControlAddress(sAddress, 2) If Len(sAddr) <> 4 Then Return bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand.Add(&H80) TransmitData(bCommand) Case Else If InStr(sValue, "DIM ") Then ' DIM 0-100 iBrightness = Val(Replace(sValue, "DIM ", "")) ' extract third address and convert it to knx format sAddr = CreateControlAddress(sAddress, 3) If Len(sAddr) <> 4 Then Return bCommand[2] = Val("&H" & Left(sAddr, 2)) bCommand[3] = Val("&H" & Right(sAddr, 2)) bCommand.Add(&H80) bCommand.Add(CInt(iBrightness * 2.55)) TransmitData(bCommand) Endif End Select Else WriteDebugLog("Unknown KNX devicetype '" & sDeviceTypeDesc & "'!") Endif Catch WriteDebugLog("ERROR: '" & Error.Text & "' at '" & Error.Where & "'!") End Public Function Addr2Str(sAddress As String, bGroupAddress As Boolean) As String If bGroupAddress Then ' convert to group address Return (Lsr((Val("&H" & sAddress)), 11) And &H1F) & "/" & (Lsr((Val("&H" & sAddress)), 8) And &H7) & "/" & ((Val("&H" & sAddress)) And &HFF) Else ' convert to physical address Return (Lsr((Val("&H" & sAddress)), 12) And &HF) & "." & (Lsr((Val("&H" & sAddress)), 8) And &HF) & "." & ((Val("&H" & sAddress)) And &HFF) Endif End Public Function Str2Addr(sA1 As String, sA2 As String, sA3 As String) As String Dim sString As String = Lsl(Val(sA1), 11) Or Lsl(Val(sA2), 8) Or Val(sA3) Return Hex$(sString, 4) End