From 9a6cdbe1c1cefe00e15056cf67903434503cf450 Mon Sep 17 00:00:00 2001 From: Norbert Richter Date: Sat, 11 May 2019 13:46:58 +0200 Subject: [PATCH] decode-config.py: adapt settings - fix suppress of empty values for output format "Tasmota cmnd" - add SetOption63 (no_power_feedback) - add config file verbose info - change `@f`/`@h` macro char replacement (alphanumeric chars only, same as Tasmota does) - remove mcp230xx pinmode validation - revert PowerSet, VoltageSet, CurrentSet for >= v6.2.1.6 from -T cmnd - allow mix case for -g parameter - adjust command groups -g to Tasmota Wiki --- tools/decode-config.html | 174 ++++++++++---------- tools/decode-config.md | 4 +- tools/decode-config.py | 345 +++++++++++++++++++++------------------ 3 files changed, 279 insertions(+), 244 deletions(-) diff --git a/tools/decode-config.html b/tools/decode-config.html index 68968f00d..4dc80286b 100644 --- a/tools/decode-config.html +++ b/tools/decode-config.html @@ -211,109 +211,109 @@ If you do not want using auto extensions use the --no-extension par

For better reading each short written arg (minus sign -) has a corresponding long version (two minus signs --), eg. --device for -d or --file for -f (note: not even all -- arg has a corresponding - one).

A short list of possible program args is displayed using -h or --help.

For advanced help use -H or --full-help:

-
usage: decode-config.py [-f <filename>] [-d <host>] [-P <port>]
-                        [-u <username>] [-p <password>] [-i <filename>]
-                        [-o <filename>] [-t json|bin|dmp] [-E] [-e] [-F]
-                        [--json-indent <indent>] [--json-compact]
-                        [--json-hide-pw] [--json-show-pw]
-                        [--cmnd-indent <indent>] [--cmnd-groups]
-                        [--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort]
-                        [-c <filename>] [-S] [-T json|cmnd|command]
-                        [-g {Display,Domoticz,Internal,KNX,Led,Logging,MCP230xx,MQTT,Main,Management,Pow,Sensor,Serial,SetOption,SonoffRF,System,Timers,Wifi} [{Display,Domoticz,Internal,KNX,Led,Logging,MCP230xx,MQTT,Main,Management,Pow,Sensor,Serial,SetOption,SonoffRF,System,Timers,Wifi} ...]]
-                        [--ignore-warnings] [-h] [-H] [-v] [-V]
+
usage: decode-config.py [-f <filename>] [-d <host>] [-P <port>]
+                        [-u <username>] [-p <password>] [-i <filename>]
+                        [-o <filename>] [-t json|bin|dmp] [-E] [-e] [-F]
+                        [--json-indent <indent>] [--json-compact]
+                        [--json-hide-pw] [--json-show-pw]
+                        [--cmnd-indent <indent>] [--cmnd-groups]
+                        [--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort]
+                        [-c <filename>] [-S] [-T json|cmnd|command]
+                        [-g {Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} ...]]
+                        [--ignore-warnings] [-h] [-H] [-v] [-V]
 
-Backup/Restore Sonoff-Tasmota configuration data. Args that start with '--'
-(eg. -f) can also be set in a config file (specified via -c). Config file
-syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at
-https://goo.gl/R74nmi). If an arg is specified in more than one place, then
-commandline values override config file values which override defaults.
+Backup/Restore Sonoff-Tasmota configuration data. Args that start with '--'
+(eg. -f) can also be set in a config file (specified via -c). Config file
+syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at
+https://goo.gl/R74nmi). If an arg is specified in more than one place, then
+commandline values override config file values which override defaults.
 
-Source:
-  Read/Write Tasmota configuration from/to
+Source:
+  Read/Write Tasmota configuration from/to
 
-  -f, --file, --tasmota-file <filename>
-                        file to retrieve/write Tasmota configuration from/to
-                        (default: None)'
-  -d, --device, --host <host>
-                        hostname or IP address to retrieve/send Tasmota
-                        configuration from/to (default: None)
-  -P, --port <port>     TCP/IP port number to use for the host connection
-                        (default: 80)
-  -u, --username <username>
-                        host HTTP access username (default: admin)
-  -p, --password <password>
-                        host HTTP access password (default: None)
+  -f, --file, --tasmota-file <filename>
+                        file to retrieve/write Tasmota configuration from/to
+                        (default: None)'
+  -d, --device, --host <host>
+                        hostname or IP address to retrieve/send Tasmota
+                        configuration from/to (default: None)
+  -P, --port <port>     TCP/IP port number to use for the host connection
+                        (default: 80)
+  -u, --username <username>
+                        host HTTP access username (default: admin)
+  -p, --password <password>
+                        host HTTP access password (default: None)
 
-Backup/Restore:
-  Backup & restore specification
+Backup/Restore:
+  Backup & restore specification
 
-  -i, --restore-file <filename>
-                        file to restore configuration from (default: None).
-                        Replacements: @v=firmware version from config,
-                        @f=device friendly name from config, @h=device
-                        hostname from config, @H=device hostname from device
-                        (-d arg only)
-  -o, --backup-file <filename>
-                        file to backup configuration to (default: None).
-                        Replacements: @v=firmware version from config,
-                        @f=device friendly name from config, @h=device
-                        hostname from config, @H=device hostname from device
-                        (-d arg only)
-  -t, --backup-type json|bin|dmp
-                        backup filetype (default: 'json')
-  -E, --extension       append filetype extension for -i and -o filename
-                        (default)
-  -e, --no-extension    do not append filetype extension, use -i and -o
+  -i, --restore-file <filename>
+                        file to restore configuration from (default: None).
+                        Replacements: @v=firmware version from config,
+                        @f=device friendly name from config, @h=device
+                        hostname from config, @H=device hostname from device
+                        (-d arg only)
+  -o, --backup-file <filename>
+                        file to backup configuration to (default: None).
+                        Replacements: @v=firmware version from config,
+                        @f=device friendly name from config, @h=device
+                        hostname from config, @H=device hostname from device
+                        (-d arg only)
+  -t, --backup-type json|bin|dmp
+                        backup filetype (default: 'json')
+  -E, --extension       append filetype extension for -i and -o filename
+                        (default)
+  -e, --no-extension    do not append filetype extension, use -i and -o
                         filename as passed
-  -F, --force-restore   force restore even configuration is identical
+  -F, --force-restore   force restore even configuration is identical
 
-JSON output:
-  JSON format specification
+JSON output:
+  JSON format specification
 
-  --json-indent <indent>
-                        pretty-printed JSON output using indent level
-                        (default: 'None'). -1 disables indent.
-  --json-compact        compact JSON output by eliminate whitespace
-  --json-hide-pw        hide passwords
-  --json-show-pw, --json-unhide-pw
-                        unhide passwords (default)
+  --json-indent <indent>
+                        pretty-printed JSON output using indent level
+                        (default: 'None'). -1 disables indent.
+  --json-compact        compact JSON output by eliminate whitespace
+  --json-hide-pw        hide passwords
+  --json-show-pw, --json-unhide-pw
+                        unhide passwords (default)
 
-Tasmota command output:
-  Tasmota command output format specification
+Tasmota command output:
+  Tasmota command output format specification
 
-  --cmnd-indent <indent>
-                        Tasmota command grouping indent level (default: '2').
-                        0 disables indent
-  --cmnd-groups         group Tasmota commands (default)
-  --cmnd-nogroups       leave Tasmota commands ungrouped
-  --cmnd-sort           sort Tasmota commands (default)
-  --cmnd-unsort         leave Tasmota commands unsorted
+  --cmnd-indent <indent>
+                        Tasmota command grouping indent level (default: '2').
+                        0 disables indent
+  --cmnd-groups         group Tasmota commands (default)
+  --cmnd-nogroups       leave Tasmota commands ungrouped
+  --cmnd-sort           sort Tasmota commands (default)
+  --cmnd-unsort         leave Tasmota commands unsorted
 
-Common:
-  Optional arguments
+Common:
+  Optional arguments
 
-  -c, --config <filename>
-                        program config file - can be used to set default
-                        command args (default: None)
-  -S, --output          display output regardsless of backup/restore usage
-                        (default do not output on backup or restore usage)
-  -T, --output-format json|cmnd|command
-                        display output format (default: 'json')
-  -g, --group {Display,Domoticz,Internal,KNX,Led,Logging,MCP230xx,MQTT,Main,Management,Pow,Sensor,Serial,SetOption,SonoffRF,System,Timers,Wifi}
-                        limit data processing to command groups (default no
-                        filter)
-  --ignore-warnings     do not exit on warnings. Not recommended, used by your
+  -c, --config <filename>
+                        program config file - can be used to set default
+                        command args (default: None)
+  -S, --output          display output regardsless of backup/restore usage
+                        (default do not output on backup or restore usage)
+  -T, --output-format json|cmnd|command
+                        display output format (default: 'json')
+  -g, --group {Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi}
+                        limit data processing to command groups (default no
+                        filter)
+  --ignore-warnings     do not exit on warnings. Not recommended, used by your
                         own responsibility!
 
-Info:
-  Extra information
+Info:
+  Extra information
 
-  -h, --help            show usage help message and exit
-  -H, --full-help       show full help message and exit
-  -v, --verbose         produce more output about what the program does
-  -V, --version         show program's version number and exit
+  -h, --help            show usage help message and exit
+  -H, --full-help       show full help message and exit
+  -v, --verbose         produce more output about what the program does
+  -V, --version         show program's version number and exit
 
-Either argument -d <host> or -f <filename> must be given.
+Either argument -d <host> or -f <filename> must be given.
 

Program parameter notes

decode-config.py

Examples

diff --git a/tools/decode-config.md b/tools/decode-config.md index ca70d36e9..01b20a143 100644 --- a/tools/decode-config.md +++ b/tools/decode-config.md @@ -237,7 +237,7 @@ For advanced help use `-H` or `--full-help`: [--cmnd-indent ] [--cmnd-groups] [--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort] [-c ] [-S] [-T json|cmnd|command] - [-g {Display,Domoticz,Internal,KNX,Led,Logging,MCP230xx,MQTT,Main,Management,Pow,Sensor,Serial,SetOption,SonoffRF,System,Timers,Wifi} [{Display,Domoticz,Internal,KNX,Led,Logging,MCP230xx,MQTT,Main,Management,Pow,Sensor,Serial,SetOption,SonoffRF,System,Timers,Wifi} ...]] + [-g {Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} ...]] [--ignore-warnings] [-h] [-H] [-v] [-V] Backup/Restore Sonoff-Tasmota configuration data. Args that start with '--' @@ -317,7 +317,7 @@ For advanced help use `-H` or `--full-help`: (default do not output on backup or restore usage) -T, --output-format json|cmnd|command display output format (default: 'json') - -g, --group {Display,Domoticz,Internal,KNX,Led,Logging,MCP230xx,MQTT,Main,Management,Pow,Sensor,Serial,SetOption,SonoffRF,System,Timers,Wifi} + -g, --group {Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} limit data processing to command groups (default no filter) --ignore-warnings do not exit on warnings. Not recommended, used by your diff --git a/tools/decode-config.py b/tools/decode-config.py index 5bc7b1b54..d05b8ff49 100755 --- a/tools/decode-config.py +++ b/tools/decode-config.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -VER = '2.1.0024' +VER = '2.2.0025' """ decode-config.py - Backup/Restore Sonoff-Tasmota configuration data @@ -43,7 +43,7 @@ Usage: decode-config.py [-f ] [-d ] [-P ] [--cmnd-indent ] [--cmnd-groups] [--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort] [-c ] [-S] [-T json|cmnd|command] - [-g {Display,Domoticz,Internal,KNX,Led,Logging,MCP230xx,MQTT,Main,Management,Pow,Sensor,Serial,SetOption,SonoffRF,System,Timers,Wifi} [{Display,Domoticz,Internal,KNX,Led,Logging,MCP230xx,MQTT,Main,Management,Pow,Sensor,Serial,SetOption,SonoffRF,System,Timers,Wifi} ...]] + [-g {Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} ...]] [--ignore-warnings] [-h] [-H] [-v] [-V] Backup/Restore Sonoff-Tasmota configuration data. Args that start with '--' @@ -123,7 +123,7 @@ Usage: decode-config.py [-f ] [-d ] [-P ] (default do not output on backup or restore usage) -T, --output-format json|cmnd|command display output format (default: 'json') - -g, --group {Display,Domoticz,Internal,KNX,Led,Logging,MCP230xx,MQTT,Main,Management,Pow,Sensor,Serial,SetOption,SonoffRF,System,Timers,Wifi} + -g, --group {Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} limit data processing to command groups (default no filter) --ignore-warnings do not exit on warnings. Not recommended, used by your @@ -308,7 +308,7 @@ Settings dictionary describes the config file fields definition: Tasmota command definition : command group string - : + : | (,...) convert data into Tasmota command function : | (, ) @@ -407,7 +407,6 @@ def MqttFingerprint(value, idx=None): # ---------------------------------------------------------------------- # Tasmota configuration data definition # ---------------------------------------------------------------------- -Groups = ('Main','Sensor','Timers','Management','Wifi','MQTT','Serial','SetOption','Logging','Pow','Led','KNX','Domoticz','Display','MCP230xx') Setting_5_10_0 = { # , , [,] 'cfg_holder': ('0 and bitsRead($,0,11)>(12*60) else "",time=time.strftime("%H:%M",time.gmtime((bitsRead($,0,11) if bitsRead($,29,2)==0 else bitsRead($,0,11) if bitsRead($,0,11)<=(12*60) else bitsRead($,0,11)-(12*60))*60)),window=bitsRead($,11,4),repeat=bitsRead($,15),days="{:07b}".format(bitsRead($,16,7))[::-1],device=bitsRead($,23,4)+1,power=bitsRead($,27,2) )')), ('"0x{:08x}".format($)', False) ), - 'time': ('0 and bitsRead($,0,11)>(12*60) else "",time=time.strftime("%H:%M",time.gmtime((bitsRead($,0,11) if bitsRead($,29,2)==0 else bitsRead($,0,11) if bitsRead($,0,11)<=(12*60) else bitsRead($,0,11)-(12*60))*60)),window=bitsRead($,11,4),repeat=bitsRead($,15),days="{:07b}".format(bitsRead($,16,7))[::-1],device=bitsRead($,23,4)+1,power=bitsRead($,27,2) )')), ('"0x{:08x}".format($)', False) ), + 'time': ('= 0 and args.device is not None: device_hostname = GetTasmotaHostname(args.device, args.port, username=args.username, password=args.password) if device_hostname is None: @@ -1493,7 +1501,6 @@ def PushTasmotaConfig(encode_cfg, host, port, username=DEFAULTS['source']['usern # post data c = pycurl.Curl() header = HTTPHeader() - from StringIO import StringIO buffer_ = io.BytesIO() c.setopt(c.HEADERFUNCTION, header.store) c.setopt(c.WRITEFUNCTION, lambda x: None) @@ -1665,9 +1672,16 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da if group is not None and not isinstance(group, (str, unicode)): print >> sys.stderr, 'wrong {} in {}'.format(group, fielddef) raise SyntaxError(' error') - if tasmotacmnd is not None and not callable(tasmotacmnd) and not isinstance(tasmotacmnd, (str, unicode)): - print >> sys.stderr, 'wrong {} in {}'.format(tasmotacmnd, fielddef) - raise SyntaxError(' error') + if tasmotacmnd is isinstance(tasmotacmnd, tuple): + tasmotacmnds = tasmotacmnd + for tasmotacmnd in tasmotacmnds: + if tasmotacmnd is not None and not callable(tasmotacmnd) and not isinstance(tasmotacmnd, (str, unicode)): + print >> sys.stderr, 'wrong {} in {}'.format(tasmotacmnd, fielddef) + raise SyntaxError(' error') + else: + if tasmotacmnd is not None and not callable(tasmotacmnd) and not isinstance(tasmotacmnd, (str, unicode)): + print >> sys.stderr, 'wrong {} in {}'.format(tasmotacmnd, fielddef) + raise SyntaxError(' error') else: print >> sys.stderr, 'wrong {} length ({}) in {}'.format(cmd, len(cmd), fielddef) raise SyntaxError(' error') @@ -1991,10 +2005,13 @@ def IsFilterGroup(group): @return: True if group is in filter, otherwise False """ + if args.filter is not None: if group is None: return False - if group != INTERNAL and group != '*' and group not in args.filter: + if group == '*': + return False + if group.title() != INTERNAL.title() and group.title() not in (groupname.title() for groupname in args.filter): return False return True @@ -2373,12 +2390,20 @@ def SetCmnd(cmnds, fieldname, fielddef, valuemapping, mappedvalue, addroffset=0, # a simple value elif isinstance(format_, (str, bool, int, float, long)): - cmnd = CmndConverter(valuemapping, mappedvalue, idx, fielddef) - - if group is not None and cmnd is not None: - if group not in cmnds: - cmnds[group] = [] - cmnds[group].append(cmnd) + if isinstance(tasmotacmnd, tuple): + tasmotacmnds = tasmotacmnd + for tasmotacmnd in tasmotacmnds: + cmnd = CmndConverter(valuemapping, mappedvalue, idx, fielddef) + if group is not None and cmnd is not None: + if group not in cmnds: + cmnds[group] = [] + cmnds[group].append(cmnd) + else: + cmnd = CmndConverter(valuemapping, mappedvalue, idx, fielddef) + if group is not None and cmnd is not None: + if group not in cmnds: + cmnds[group] = [] + cmnds[group].append(cmnd) return cmnds @@ -2401,7 +2426,7 @@ def Bin2Mapping(decode_cfg): # if we did not found a mathching setting if setting is None: - exit(ExitCode.UNSUPPORTED_VERSION, "Tasmota configuration version 0x{:x} not supported".format(version),line=inspect.getlineno(inspect.currentframe())) + exit(ExitCode.UNSUPPORTED_VERSION, "Tasmota configuration version {} not supported".format(version),line=inspect.getlineno(inspect.currentframe())) if 'version' in setting: cfg_version = GetField(decode_cfg, 'version', setting['version'], raw=True) @@ -2655,7 +2680,7 @@ def Restore(restorefile, backupfileformat, encode_cfg, decode_cfg, configmapping elif filetype == FileType.BIN: if args.verbose: - message("Reading restore file '{}' (binary format)".format(restorefilename), typ=LogType.INFO) + message("Reading restore file '{}' (Binary format)".format(restorefilename), typ=LogType.INFO) try: with open(restorefilename, "rb") as restorefp: restorebin = restorefp.read() @@ -2688,6 +2713,13 @@ def Restore(restorefile, backupfileformat, encode_cfg, decode_cfg, configmapping exit(ExitCode.FILE_READ_ERROR, "File '{}' unknown error".format(restorefilename),line=inspect.getlineno(inspect.currentframe())) if new_encode_cfg is not None: + if args.verbose: + new_decode_cfg = DecryptEncrypt(new_encode_cfg) + # get binary header and template to use + version, size, setting = GetTemplateSetting(new_decode_cfg) + # get config file version + cfg_version = GetField(new_decode_cfg, 'version', setting['version'], raw=True) + message("Config file contains data of Sonoff-Tasmota {}".format(GetVersionStr(cfg_version)), typ=LogType.INFO) if args.forcerestore or new_encode_cfg != encode_cfg: # write config direct to device via http if args.device is not None: @@ -2734,9 +2766,11 @@ def OutputTasmotaCmnds(tasmotacmnds): for cmnd in cmnds: print "{}{}".format(" "*args.cmndindent, cmnd) + groups = GetGroupList(Settings[0][2]) + if args.cmndgroup: - for group in Groups: - if group in tasmotacmnds: + for group in groups: + if group.title() in (groupname.title() for groupname in tasmotacmnds): cmnds = tasmotacmnds[group] print print "# {}:".format(group) @@ -2744,8 +2778,8 @@ def OutputTasmotaCmnds(tasmotacmnds): else: cmnds = [] - for group in Groups: - if group in tasmotacmnds: + for group in groups: + if group.title() in (groupname.title() for groupname in tasmotacmnds): cmnds.extend(tasmotacmnds[group]) OutputTasmotaSubCmnds(cmnds) @@ -2913,6 +2947,7 @@ def ParseArgs(): dest='filter', choices=groups, nargs='+', + type=lambda s : s.title(), default=DEFAULTS['common']['filter'], help="limit data processing to command groups (default {})".format("no filter" if DEFAULTS['common']['filter'] == None else DEFAULTS['common']['filter']) ) common.add_argument('--ignore-warnings', @@ -2993,7 +3028,7 @@ if __name__ == "__main__": # decode into mappings dictionary configmapping = Bin2Mapping(decode_cfg) if args.verbose and 'version' in configmapping: - message("{} '{}' is using version {}".format('File' if args.tasmotafile is not None else 'Device', + message("{} '{}' is using Sonoff-Tasmota {}".format('File' if args.tasmotafile is not None else 'Device', args.tasmotafile if args.tasmotafile is not None else args.device, GetVersionStr(configmapping['version'])), typ=LogType.INFO)