# # A tool for converting Pronto format hex codes to lircd.conf format. Version 1.11 # # Version History: # 1.11 - Made more resiliant against whitespace imperfections in input files # 1.1 - Added support for CCFTools/CCFDecompiler XML files and multiple devices # 1.0 - Initial release # # Copyright by Olavi Akerman # # pronto2lirc is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # import string class CodeSequence: # Handles codesequences parsing and conversion def ProcessPreamble(self,sPreamble): if sPreamble[0]<>"0000": raise "Formats other than starting with 0000 are not supported!" self.dIRFrequency=1000000/(long(sPreamble[1],16) * 0.241246) # Frequency of the IR carrier in Khz self.lOnceSequenceLength=long(sPreamble[2],16) # No of pulses that is sent once when button is pressed self.lRepeatableSequenceLength=long(sPreamble[3],16) # No of pulses that are repeatable while button pressed def CreatePulses(self,sItems): self.dPulseWidths=[] # Table of Pulse widths. Length is repsented in microseconds for i in sItems: self.dPulseWidths.append(1000000*long(i,16)/self.dIRFrequency) # Convert pulse widths to uS if len(self.dPulseWidths)<>2*(self.lOnceSequenceLength+self.lRepeatableSequenceLength): raise "Number of actual codes does not match the header information!" def AnalyzeCode(self,sCodeName,sHexCodes): # sHexTable=sHexCodes.split() s=string.join(sHexCodes.split(),'') # Remove whitespace formatting between sequences sHexTable=[] while s<>'': # Re-split into group of 4 sHexTable.append(s[:4]) s=s[4:] self.sCodeName=sCodeName.rstrip() # Name of the Code associated with code sequence self.ProcessPreamble(sHexTable[:4]) # First four sequences make up Preamble self.CreatePulses(sHexTable[4:]) # The rest are OnceSequence + RepeatableSequence return self.dPulseWidths[-1] # Final gap=last off signal length def WriteCodeSection(self,fOut): fOut.write('\n\t\t\tname '+self.sCodeName.strip().replace(' ','')+'\n') # Can't contain whitespace for i in range(len(self.dPulseWidths)-1): # Do not write the last signal as lircd.conf # does not contain last off signal length if (i%6) ==0: fOut.write('\t\t\t\t') fOut.write('%d ' % round(self.dPulseWidths[i])) if (i+1)%6 ==0: # Group codes as six per line fOut.write('\n') fOut.write('\n') # Final EOL class Device: # Handles devices def AddCodes(self,sCodeName,sHexCodes): # Add new code sequence seq=CodeSequence() finalgap=seq.AnalyzeCode(sCodeName,sHexCodes) if finalgap>self.lGap: self.lGap=finalgap self.sCodes.append(seq) def ProcessHEX(self,fHexFile,sLine): # Process HEX files while sLine<>'' and sLine.strip()<>'': # EOF? [sCodeName,sHexCodes]=sLine.split(':') self.AddCodes(sCodeName,sHexCodes) sLine=fHexFile.readline() def WriteLIRCCConfDevice(self,f): f.write('begin remote\n') f.write('\tname\t'+self.sDeviceName.replace(' ','')+'\n') f.write('\tflags\tRAW_CODES\n') f.write('\teps\t30\n') f.write('\taeps\t100\n') f.write('\tgap\t%d\n' % self.lGap ) f.write('\t\tbegin raw_codes\n') for i in self.sCodes: i.WriteCodeSection(f) f.write('\t\tend raw_codes\n') f.write('end remote\n') def __init__(self,sDeviceName): self.sDeviceName=sDeviceName # Name of the device self.sCodes=[] # Codes contained in file self.lGap=0 # Final Gap class RemoteFilesParser: def ProcessXML(self,fXMLFile): def start_element(name,attrs): if name=="RAWCODE": self.Devices[-1].AddCodes(attrs['name'],attrs['data']) if name=="DEVICE": self.Devices.append(Device(attrs['name'])) p = xml.parsers.expat.ParserCreate() p.StartElementHandler = start_element fXMLFile.seek(0) # Need to start from the beginning p.ParseFile(fXMLFile) def __init__(self,sFileName): self.Devices=[] f=open(sFileName,'r') sLine=f.readline() if sLine.strip()=='': # Are we dealing with CCF Decompiler XML file? self.ProcessXML(f) else: device=Device(sFileName.split('.')[:1][0]) self.Devices.append(device) device.ProcessHEX(f,sLine) f.close() def WriteLIRCConf(self,sOutFileName): f=open(sOutFileName,'w') for d in self.Devices: d.WriteLIRCCConfDevice(f) f.close() # Main import sys import xml.parsers.expat if len(sys.argv)<>2: print "Pronto codes converter to lircd.conf format (version 1.11)" print print "Usage: pronto2lirc.py inputfile" print print "Inputfile can be:" print " 1.An xml file output by CCFTools/CCFDecompiler." print " 2.Text file where each line contains the name of the button and " print " all codes associated with it" print " Button1:0000 00ac 000b 00de ..." print print "Result: lircd.conf file is written to the current directory" print " containing all the Pronto codes extracted from" print " the input file" print else: p=RemoteFilesParser(sys.argv[1]) p.WriteLIRCConf('lircd.conf')