diff --git a/tools/decode-config.py b/tools/decode-config.py index f3e2b526b..94d0cc4fa 100755 --- a/tools/decode-config.py +++ b/tools/decode-config.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -VER = '2.2.0026' +VER = '2.2.0027' """ decode-config.py - Backup/Restore Sonoff-Tasmota configuration data @@ -908,7 +908,19 @@ Setting_6_5_0_11['flag3'][0].update ({ 'use_underscore': ('> sys.stderr, '{styp}{sdelimiter}{sstatus}{slineno}{scolon}{smgs}'.format(\ - styp=typ if typ is not None else '', - sdelimiter=' ' if status is not None and status > 0 and typ is not None else '', + styp=type_ if type_ is not None else '', + sdelimiter=' ' if status is not None and status > 0 and type_ is not None else '', sstatus=status if status is not None and status > 0 else '', - scolon=': ' if typ is not None or line is not None else '', + scolon=': ' if type_ is not None or line is not None else '', smgs=msg, slineno=' (@{:04d})'.format(line) if line is not None else '') -def exit(status=0, msg="end", typ=LogType.ERROR, src=None, doexit=True, line=None): +def exit(status=0, msg="end", type_=LogType.ERROR, src=None, doexit=True, line=None): """ Called when the program should be exit @@ -987,7 +999,7 @@ def exit(status=0, msg="end", typ=LogType.ERROR, src=None, doexit=True, line=Non the exit status program returns to callert @param msg: the msg logged before exit - @param typ: + @param type_: msg type: 'INFO', 'WARNING' or 'ERROR' @param doexit: True to exit program, otherwise return @@ -995,7 +1007,7 @@ def exit(status=0, msg="end", typ=LogType.ERROR, src=None, doexit=True, line=Non if src is not None: msg = '{} ({})'.format(src, msg) - message(msg, typ=typ if status!=ExitCode.OK else LogType.INFO, status=status, line=line) + message(msg, type_=type_ if status!=ExitCode.OK else LogType.INFO, status=status, line=line) exitcode = status if doexit: sys.exit(exitcode) @@ -1451,7 +1463,7 @@ def GetTasmotaHostname(host, port, username=DEFAULTS['source']['username'], pass if "StatusNET" in jsonbody and "Hostname" in jsonbody["StatusNET"]: hostname = jsonbody["StatusNET"]["Hostname"] if args.verbose: - message("Hostname for '{}' retrieved: '{}'".format(host, hostname), typ=LogType.INFO) + message("Hostname for '{}' retrieved: '{}'".format(host, hostname), type_=LogType.INFO) return hostname @@ -1759,7 +1771,7 @@ def ReadWriteConverter(value, fielddef, read=True, raw=False): elif callable(conv): # use as format function return conv(value) except Exception, e: - exit(e[0], e[1], typ=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) + exit(e[0], e[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) return value @@ -2082,15 +2094,33 @@ def SetFieldValue(fielddef, dobj, addr, value): addr += (bitsize / 8) * formatcnt for _ in range(0, formatcnt): addr -= (bitsize / 8) - val = value & ((2**bitsize) - 1) + maxunsigned = ((2**bitsize) - 1) + maxsigned = ((2**bitsize)>>1)-1 + val = value & maxunsigned + if isinstance(value,int) and value < 0 and val > maxsigned: + val = ((maxunsigned+1)-val) * (-1) if args.debug >= 3: print >> sys.stderr, "SetFieldValue(): Single type - fielddef {}, addr 0x{:04x} value {} singletype {} bitsize {}".format(fielddef,addr,val,singletype,bitsize) - struct.pack_into(singletype, dobj, addr, val) + try: + struct.pack_into(singletype, dobj, addr, val) + except struct.error as e: + exit(ExitCode.RESTORE_DATA_ERROR, + "Single type {} [fielddef={}, addr=0x{:04x}, value={}] - skipped!".format(e,fielddef,addr,val), + type_=LogType.WARNING, + doexit=not args.ignorewarning, + line=inspect.getlineno(inspect.currentframe())) value >>= bitsize else: if args.debug >= 3: print >> sys.stderr, "SetFieldValue(): String type - fielddef {}, addr 0x{:04x} value {} format_ {}".format(fielddef,addr,value,format_) - struct.pack_into(format_, dobj, addr, value) + try: + struct.pack_into(format_, dobj, addr, value) + except struct.error as e: + exit(ExitCode.RESTORE_DATA_ERROR, + "String type {} [fielddef={}, addr=0x{:04x}, value={}} - skipped!".format(e,fielddef,addr,value), + type_=LogType.WARNING, + doexit=not args.ignorewarning, + line=inspect.getlineno(inspect.currentframe())) return dobj @@ -2156,7 +2186,7 @@ def GetField(dobj, fieldname, fielddef, raw=False, addroffset=0): valuemapping = ReadWriteConverter(GetFieldValue(fielddef, dobj, baseaddr+addroffset), fielddef, read=True, raw=raw) else: - exit(ExitCode.INTERNAL_ERROR, "Wrong mapping format definition: '{}'".format(format_), typ=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) + exit(ExitCode.INTERNAL_ERROR, "Wrong mapping format definition: '{}'".format(format_), type_=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) return valuemapping @@ -2199,7 +2229,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): if isinstance(arraydef, list) and len(arraydef) > 0: offset = 0 if len(restore) > arraydef[0]: - exit(ExitCode.RESTORE_DATA_ERROR, "file '{sfile}', array '{sname}[{selem}]' exceeds max number of elements [{smax}]".format(sfile=filename, sname=fieldname, selem=len(restore), smax=arraydef[0]), typ=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) + exit(ExitCode.RESTORE_DATA_ERROR, "file '{sfile}', array '{sname}[{selem}]' exceeds max number of elements [{smax}]".format(sfile=filename, sname=fieldname, selem=len(restore), smax=arraydef[0]), type_=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) for i in range(0, arraydef[0]): subfielddef = GetSubfieldDef(fielddef) length = GetFieldLength(subfielddef) @@ -2231,7 +2261,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): try: value = ReadWriteConverter(restore.encode(STR_ENCODING)[0], fielddef, read=False) except Exception, e: - exit(e[0], e[1], typ=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) + exit(e[0], e[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) valid = False # bool @@ -2239,7 +2269,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): try: value = ReadWriteConverter(bool(restore), fielddef, read=False) except Exception, e: - exit(e[0], e[1], typ=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) + exit(e[0], e[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) valid = False # integer @@ -2331,13 +2361,13 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""): dobj = SetFieldValue(fielddef, dobj, baseaddr+addroffset, value) curvalue = GetFieldValue(fielddef, dobj, baseaddr+addroffset) if prevvalue != curvalue and args.verbose: - message("Value for '{}' changed from {} to {}".format(fieldname, prevvalue, curvalue), typ=LogType.INFO) + message("Value for '{}' changed from {} to {}".format(fieldname, prevvalue, curvalue), type_=LogType.INFO) else: if args.debug >= 2: print >> sys.stderr, "SetField(): Special field '{}' using '{}'/{}{} @{} skipped".format(fieldname, format_, arraydef, bits, hex(baseaddr+addroffset)) else: sformat = "file '{sfile}' - {{'{sname}': {svalue}}} ({serror})"+errformat - exit(ExitCode.RESTORE_DATA_ERROR, sformat.format(sfile=filename, sname=fieldname, serror=err, svalue=_value, smin=min_, smax=max_), typ=LogType.WARNING, doexit=not args.ignorewarning) + exit(ExitCode.RESTORE_DATA_ERROR, sformat.format(sfile=filename, sname=fieldname, serror=err, svalue=_value, smin=min_, smax=max_), type_=LogType.WARNING, doexit=not args.ignorewarning) return dobj @@ -2377,7 +2407,7 @@ def SetCmnd(cmnds, fieldname, fielddef, valuemapping, mappedvalue, addroffset=0, if isinstance(arraydef, list) and len(arraydef) > 0: offset = 0 if len(mappedvalue) > arraydef[0]: - exit(ExitCode.RESTORE_DATA_ERROR, "array '{sname}[{selem}]' exceeds max number of elements [{smax}]".format(sname=fieldname, selem=len(mappedvalue), smax=arraydef[0]), typ=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) + exit(ExitCode.RESTORE_DATA_ERROR, "array '{sname}[{selem}]' exceeds max number of elements [{smax}]".format(sname=fieldname, selem=len(mappedvalue), smax=arraydef[0]), type_=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) for i in range(0, arraydef[0]): subfielddef = GetSubfieldDef(fielddef) length = GetFieldLength(subfielddef) @@ -2443,10 +2473,10 @@ def Bin2Mapping(decode_cfg): # read size should be same as definied in setting if cfg_size > size: # may be processed - exit(ExitCode.DATA_SIZE_MISMATCH, "Number of bytes read does ot match - read {}, expected {} byte".format(cfg_size, size), typ=LogType.ERROR,line=inspect.getlineno(inspect.currentframe())) + exit(ExitCode.DATA_SIZE_MISMATCH, "Number of bytes read does ot match - read {}, expected {} byte".format(cfg_size, size), type_=LogType.ERROR,line=inspect.getlineno(inspect.currentframe())) elif cfg_size < size: # less number of bytes can not be processed - exit(ExitCode.DATA_SIZE_MISMATCH, "Number of bytes read to small to process - read {}, expected {} byte".format(cfg_size, size), typ=LogType.ERROR,line=inspect.getlineno(inspect.currentframe())) + exit(ExitCode.DATA_SIZE_MISMATCH, "Number of bytes read to small to process - read {}, expected {} byte".format(cfg_size, size), type_=LogType.ERROR,line=inspect.getlineno(inspect.currentframe())) # check crc if exists if 'cfg_crc' in setting: @@ -2454,7 +2484,7 @@ def Bin2Mapping(decode_cfg): else: cfg_crc = GetSettingsCrc(decode_cfg) if cfg_crc != GetSettingsCrc(decode_cfg): - exit(ExitCode.DATA_CRC_ERROR, 'Data CRC error, read 0x{:x} should be 0x{:x}'.format(cfg_crc, GetSettingsCrc(decode_cfg)), typ=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe())) + exit(ExitCode.DATA_CRC_ERROR, 'Data CRC error, read 0x{:x} should be 0x{:x}'.format(cfg_crc, GetSettingsCrc(decode_cfg)), type_=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe())) # get valuemapping valuemapping = GetField(decode_cfg, None, (setting,0,(None, None, (INTERNAL, None)))) @@ -2525,7 +2555,7 @@ def Mapping2Bin(decode_cfg, jsonconfig, filename=""): SetField(_buffer, name, setting[name], jsonconfig[name], addroffset=0, filename=filename) else: if name != 'header': - exit(ExitCode.RESTORE_DATA_ERROR, "Restore file '{}' contains obsolete name '{}', skipped".format(filename, name), typ=LogType.WARNING, doexit=not args.ignorewarning) + exit(ExitCode.RESTORE_DATA_ERROR, "Restore file '{}' contains obsolete name '{}', skipped".format(filename, name), type_=LogType.WARNING, doexit=not args.ignorewarning) if 'cfg_crc' in setting: crc = GetSettingsCrc(_buffer) @@ -2533,7 +2563,7 @@ def Mapping2Bin(decode_cfg, jsonconfig, filename=""): return _buffer else: - exit(ExitCode.UNSUPPORTED_VERSION,"File '{}', Tasmota configuration version 0x{:x} not supported".format(filename, version), typ=LogType.WARNING, doexit=not args.ignorewarning) + exit(ExitCode.UNSUPPORTED_VERSION,"File '{}', Tasmota configuration version 0x{:x} not supported".format(filename, version), type_=LogType.WARNING, doexit=not args.ignorewarning) return None @@ -2568,12 +2598,12 @@ def Mapping2Cmnd(decode_cfg, valuemapping, filename=""): cmnds = SetCmnd(cmnds, name, setting[name], valuemapping, valuemapping[name], addroffset=0) else: if name != 'header': - exit(ExitCode.RESTORE_DATA_ERROR, "Restore file '{}' contains obsolete name '{}', skipped".format(filename, name), typ=LogType.WARNING, doexit=not args.ignorewarning) + exit(ExitCode.RESTORE_DATA_ERROR, "Restore file '{}' contains obsolete name '{}', skipped".format(filename, name), type_=LogType.WARNING, doexit=not args.ignorewarning) return cmnds else: - exit(ExitCode.UNSUPPORTED_VERSION,"File '{}', Tasmota configuration version 0x{:x} not supported".format(filename, version), typ=LogType.WARNING, doexit=not args.ignorewarning) + exit(ExitCode.UNSUPPORTED_VERSION,"File '{}', Tasmota configuration version 0x{:x} not supported".format(filename, version), type_=LogType.WARNING, doexit=not args.ignorewarning) return None @@ -2608,7 +2638,7 @@ def Backup(backupfile, backupfileformat, encode_cfg, decode_cfg, configmapping): fileformat = "Tasmota" backup_filename = MakeFilename(backupfile, FileType.DMP, configmapping) if args.verbose: - message("Writing backup file '{}' ({} format)".format(backup_filename, fileformat), typ=LogType.INFO) + message("Writing backup file '{}' ({} format)".format(backup_filename, fileformat), type_=LogType.INFO) try: with open(backup_filename, "wb") as backupfp: backupfp.write(encode_cfg) @@ -2620,7 +2650,7 @@ def Backup(backupfile, backupfileformat, encode_cfg, decode_cfg, configmapping): fileformat = "binary" backup_filename = MakeFilename(backupfile, FileType.BIN, configmapping) if args.verbose: - message("Writing backup file '{}' ({} format)".format(backup_filename, fileformat), typ=LogType.INFO) + message("Writing backup file '{}' ({} format)".format(backup_filename, fileformat), type_=LogType.INFO) try: with open(backup_filename, "wb") as backupfp: backupfp.write(struct.pack('