Announcement

Collapse
No announcement yet.

Broadlink - Pronto Hex to Hex conversion issue

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Broadlink - Pronto Hex to Hex conversion issue

    Hi Michael,

    Been working on converting all my devices with your plugin but uncovered an issue with the plugin Pronto Hex conversion
    ( ... I knew the long A/C codes would generate some issues...)

    So here it is,

    not sure if you developed your own conversion code or are reusing some other code but here is an example:

    Original/Working Pronto Hex code:

    Code:
    0000 006D 0046 0000 015B 00AE 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0041 0016 0016 0016 0016 0016 0041 0016 0016 0016 02FA 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 02FA

    Using MCSMQTT/Broadlink to convert - This Code doesn't work
    Code:
    26008E0000012C96131313131338131313131313131313131313131313131313131313131313131313131313131313131313133813131313131313131313131313381313133813131313133813131300029213131338131313131313131313131313131313131313131313131313133813131313131313131313131313131313131313131313131313131313133813131313130002920D050000000000000000

    Using script below to convert ( I made it available to run online : jdoodle.com/ia/FtY)
    This Code works when manually entered in Broadlink.ini
    Code:
    2600920000012b96131313131338131313131313131313131313131313131313131313131313131313131313131313131313133813131313131313131313131313381313133813131313133813131300029113131338131313131313131313131313131313131313131313131313133813131313131313131313131313131313131313131313131313131313133813131313130002910d0500000000
    So there is something different in the way the ProntoHex to RawHex conversion is done with the plugin and unfortunately some resulting codes are not working.
    I've tried playing with repeat/pulse with no success.

    Any way you can compare both conversion methods (yours/below) to pinpoint the differences and hopefully fix this?
    Let me know what you think, thx


    Code:
    CodesToConvert = [
      # this is one code
      "0000 006D 0046 0000 015B 00AE 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0041 0016 0016 0016 0016 0016 0041 0016 0016 0016 02FA 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 02FA",
      # this is another     code (its actually a copy of the first  code above just as an example, need a comma to seperate codes)
      "0000 006D 0046 0000 015B 00AE 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0041 0016 0016 0016 0016 0016 0041 0016 0016 0016 02FA 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 02FA"]
    
    import binascii
    import struct
    import sys
    import base64
    
    
    
    def pronto2lirc(pronto):
        codes = [long(binascii.hexlify(pronto[i:i+2]), 16) for i in xrange(0, len(pronto), 2)]
    
        if codes[0]:
            raise ValueError('Pronto code should start with 0000')
        if len(codes) != 4 + 2 * (codes[2] + codes[3]):
            raise ValueError('Number of pulse widths does not match the preamble')
    
        frequency = 1 / (codes[1] * 0.241246)
        return [int(round(code / frequency)) for code in codes[4:]]
    
    def lirc2broadlink(pulses):
        array = bytearray()
    
        for pulse in pulses:
            pulse = pulse * 269 / 8192  # 32.84ms units
    
            if pulse < 256:
                array += bytearray(struct.pack('>B', pulse))  # big endian (1-byte)
            else:
                array += bytearray([0x00])  # indicate next number is 2-bytes
                array += bytearray(struct.pack('>H', pulse))  # big endian (2-bytes)
    
        packet = bytearray([0x26, 0x00])  # 0x26 = IR, 0x00 = no repeats
        packet += bytearray(struct.pack('<H', len(array)))  # little endian byte count
        packet += array
        packet += bytearray([0x0d, 0x05])  # IR terminator
    
        # Add 0s to make ultimate packet size a multiple of 16 for 128-bit AES encryption.
        remainder = (len(packet) + 4) % 16  # rm.send_data() adds 4-byte header (02 00 00 00)
        if remainder:
            packet += bytearray(16 - remainder)
    
        return packet
    
    for code in CodesToConvert:
        # get rid of spaces in the code.
        cleanCode = code.replace(" ","")
        # pronto2lirc needs a byte array, convert it
        pronto = bytearray.fromhex(cleanCode)
        # convert the pronto to LIRC pulse format
        pulses = pronto2lirc(pronto)
        # convert LIRC pulse format to broadlink
        packet = lirc2broadlink(pulses)
    
        print
        print binascii.hexlify(packet) #print Raw Hex
        print ""
        print base64.b64encode(packet) #print base64
    
    
    # I'm not using this.
    # but it was in the original code so I'm leaving it
    # if someone compiles a python executable it uses the code below.
    if __name__ == '__main__':
    
        for code in sys.argv[1:]:
            pronto = bytearray.fromhex(code)
            pulses = pronto2lirc(pronto)
            packet = lirc2broadlink(pulses)
    
            print
            print binascii.hexlify(packet)
    ​


    Original references:
    https://community.home-assistant.io/...-codes/48391/6
    https://community.home-assistant.io/...aml/47536/3​

    #2
    The difference is in the end with lower vs. upper case, four extra bytes, and 02 vs. 01 as the last pulse.
    The lower vs. upper should be handled the same way as they are just hex representation
    The extra bytes are padding needed for AES encryption algorithm. They also should not affect the pulse stream.
    That leaves the 01 vs. 02 as the last pulse encoding before the 0D 05 end marker. Hard to believe it would make a difference.
    Code:
    02 0D 05 00 00 00 00 00 00 00 00
    01 0d 05 00 00 00 00
    Edit the .ini so your code has 02 rather than 01 before the 0d05 end marker. It that works then the difference is the padding. Another test is to take the one the plugin converted and change the 02 to 01 to assess if the padding matters.

    I did start with code from a Google search. It likely was Python or Java that I had to convert to VB.Net so variance should be language implementation differences.

    Code:
        Private Sub ConvertToBroadlink(ByVal sValue As String)
            'inpurt pronto hex code
            'Samsung TV Power
            '0000 006C 0000 0022 00AD 00AD 0016 0041 0016 0041 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0041 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0016 0041 0016 0016 0016 0041 0016 0041 0016 0041 0016 0041 0016 0041 0016 0041 0016 06FB
            'always 0000
            '     Freq
            '          onetime burst length
            '               repeat burst length
            '                     first burst pair
            If sValue.Length < 4 OrElse Not sValue.StartsWith("0000") Then
                BroadlinkFeedback("Pronto code must start with 0000")
                Exit Sub
            End If
    
            Dim arrByteString() As String = sValue.Split(" ")
            If arrByteString.Length < 2 Then
                BroadlinkFeedback("Pronto code cannot be under four bytes")
                Exit Sub
            End If
            Dim iLength As UInt16 = arrByteString.Length - 4
            Dim nCode As Double
            Dim iCode As Integer
            If Not Int32.TryParse(arrByteString(1), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, iCode) Then
                BroadlinkFeedback("Pronto frequency " & arrByteString(1) & " is not valid hex")
                Exit Sub
            End If
            nCode = CType(iCode, Double)
            Dim frequency As Double = 1.0 / (nCode * 0.241246)
    
    
            Dim arrCodes(iLength + 6 - 1) As Double  '4 Byte header, 2 Byte trailer, ubound index
            Dim iCodeIndex As Integer = 0
            For i As Integer = 4 To arrByteString.Length - 1
                If Not Int32.TryParse(arrByteString(i), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, iCode) Then
                    BroadlinkFeedback("Pronto code " & arrByteString(i) & " is not valid hex")
                    Exit Sub
                End If
                nCode = CType(iCode, Double)
                arrCodes(iCodeIndex) = Math.Round(nCode / frequency, 0)
                iCodeIndex += 1
            Next
    
            Dim arrBytes() As Byte
            ReDim arrBytes(iLength + 4 + 2 - 1)
    
            Dim iByteIndex As Integer = 4
            For i As Integer = 0 To iLength - 1
    
                Dim iPulse As Integer = CType(arrCodes(i) * 269.0 / 8192.0, Integer) '32.84 ms units
    
                If iPulse < 256 Then
                    arrBytes(iByteIndex) = CType(iPulse, Byte)
                    iByteIndex += 1
                Else
                    ReDim Preserve arrBytes(arrBytes.Length + 1) 'add two bytes
                    arrBytes(iByteIndex) = 0
                    arrBytes(iByteIndex + 1) = (iPulse >> 8) And &HFF
                    arrBytes(iByteIndex + 2) = iPulse And &HFF
                    iByteIndex += 3
                End If
            Next
    
            iLength += 2
            arrBytes(0) = &H26
            arrBytes(1) = 0
            arrBytes(2) = iLength And &HFF 'ByteIndex And &HFF
            arrBytes(3) = (iLength >> 8) And &HFF ' (iByteIndex >> 8) And &HFF
            arrBytes(iByteIndex) = &HD
            arrBytes(iByteIndex + 1) = &H5
            iByteIndex = arrBytes.Length
    
            'pad byte array so multiple of 16 for AES encryption
            Dim iMod As Integer = arrBytes.Length Mod 16
            If iMod > 0 Then
                ReDim Preserve arrBytes(arrBytes.Length + (16 - iMod) - 1)
                While iByteIndex < arrBytes.Length
                    arrBytes(iByteIndex) = 0
                    iByteIndex += 1
                End While
            End If
    
            'Dim sBase64 As String = System.Convert.ToBase64String(arrBytes)
            'Dim arrBase64() As Byte = System.Text.Encoding.UTF8.GetBytes(sBase64)
            gBroadlinkLeanType = BroadlinkLeanType.PRONTO
            AddCommand(arrBytes)
        End Sub​

    Comment


      #3
      There are other differences at the start as well:

      26008E0000012C96
      vs
      2600920000012b96

      Will try more codes tomorrow + suggested testing sequence but so far it does make a difference.

      ​​​​​​Stay tuned.

      Comment


        #4
        8E vs. 92 is the length so that is expected. I overlooked the 2C vs. 2B. My guess in this and 01 vs. 02 is a difference in how rounding is handled between the different language implementations.

        Comment


          #5
          See test results attached.
          • External converter (Python script above) and all manually modified sub-versions work.
          • Internal converter (Plugin) and all manually modified sub-versions do not work.
          • Similar code learned via Broadlink+Plugin does work & seems to display the same lenght/conversion pattern.
          Let me know if you can pinpoint the issue.

          Thx,
          Attached Files

          Comment


            #6
            Am I correct in the interpretation of test results that the issue is the plug-in adds a set of 00 codes at the end for the case where the length is already a multiple of 16 bytes. If so that should be an easy fix.

            Comment


              #7
              In 6.3.1.0 I removed the leading 00 constraint and changed the padding algorithm. https://forums.homeseer.com/forum/hs...ge-log-hs4-hs3

              Comment


                #8
                Thank you, see attached/below:

                still 2 slight differences (in red) vs the external converter but it does prevent the code from working:

                Click image for larger version  Name:	image.png Views:	0 Size:	99.4 KB ID:	1599875​​

                as you previously noted, if 8E vs. 92 reflects the string length, I guess it should now be 92. Manually tested as working with 92 vs 8E.

                the other 1 vs 2 or B vs C makes no difference, all versions work as long as 92 replaces 8E.


                Attached Files

                Comment


                  #9
                  So it seems the length parameter needs to include the padding even though no pulses are being generated. I made this change but did not change the version number. It is 6.3.1.0 with links at bottom of mcsMQTT Change Log (HS4 & HS3) - HomeSeer Message Board .

                  Excellent feedback to help get this working correctly.​

                  Comment


                    #10
                    Now works perfectly, Thanks!



                    PS: If you don't mind, keep this one on the wishlist for a next release:

                    add a way to delete an appliance altogether (All codes, Library, linked HS device & MQTT association)
                    + some way to refresh a device if a single code is deleted; with the current option, it gets deleted from Broadlink.ini but stays active in the HS Device drop down list until plugin/HS restarted.

                    Comment


                      #11
                      Is your delete request for an Appliance to be deleted?
                      For the refresh there are two items. One is the selector on the Local page. The other is the VSP in HS Feature or in the case of the other model it will be the HS Feature itself. I assume you are referring to everyting. When a code is learned/added does everything get updated or does that also require a restart?

                      Comment


                        #12
                        Sorry meant "appliance". Adding codes (single or list) does not require a refresh.

                        ​​​​​ Note that I'm on HS4 but still use the legacy device manager so this could have an impact on HS behavior.

                        Comment

                        Working...
                        X