mirror of https://github.com/arendst/Tasmota.git
Merge pull request #6978 from curzon01/development
decode-config.py: adapt settings, rename Sonoff->Tasmota, prep for Python3
This commit is contained in:
commit
206e6be596
|
@ -6,7 +6,7 @@
|
|||
<li>can restore previously backup and changed <a href="http://www.json.org/">JSON</a>-format files,</li>
|
||||
<li>is able to create Tasmota compatible command list with related config parameter</li>
|
||||
</ul>
|
||||
<p>Comparing backup files created by <em>decode-config.py</em> and *.dmp files created by Tasmota "Backup/Restore Configuration": </p>
|
||||
<p>Comparing backup files created by <em>decode-config.py</em> and *.dmp files created by Tasmota "Backup/Restore Configuration":</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -75,23 +75,39 @@
|
|||
</ul>
|
||||
<h2 id="prerequisite">Prerequisite</h2>
|
||||
<ul>
|
||||
<li><p><a href="https://en.wikipedia.org/wiki/Python_(programming_language">Python</a>)<br>This program is written in <a href="https://en.wikipedia.org/wiki/Python_(programming_language">Python</a>) so you need to install a working python environment (for details see <a href="https://docs.python.org/2.7/using/index.html">Python Setup and Usage</a>)</p>
|
||||
</li>
|
||||
<li><p><a href="https://github.com/arendst/Tasmota">Tasmota</a> <a href="https://github.com/arendst/Tasmota/releases">Firmware</a> with Web-Server enabled:</p>
|
||||
<ul>
|
||||
<li>This program is written in <a href="https://en.wikipedia.org/wiki/Python_(programming_language">Python</a>) so you need to install a working python environment for your operating system.</li>
|
||||
</ul>
|
||||
<h3 id="linux">Linux</h3>
|
||||
<pre><code>sudo apt-<span class="hljs-built_in">get</span> install python python-pip libcurl4-openssl-<span class="hljs-built_in">dev</span> libssl-<span class="hljs-built_in">dev</span>
|
||||
</code></pre><pre><code>pip <span class="hljs-keyword">install</span> pycurl configargparse
|
||||
</code></pre><h3 id="windows-10">Windows 10</h3>
|
||||
<p>Install <a href="https://www.python.org/download/releases/2.7/">Python 2.7</a> then install dependencies. For PyCurl you need to <a href="https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycurl">download pycurl‑7.43.0.3‑cp27‑cp27m‑win_amd64.whl</a> for Windows 10 64bit.</p>
|
||||
<pre><code>pip install pycurl<span class="hljs-number">-7.43</span><span class="hljs-number">.0</span><span class="hljs-number">.3</span>-cp27-cp27m-win_amd64.whl configargparse
|
||||
<span class="hljs-comment">// run the command from the folder where you downloaded the file</span>
|
||||
|
||||
pip install configargparse
|
||||
</code></pre><ul>
|
||||
<li><a href="https://github.com/arendst/Tasmota">Tasmota</a> <a href="https://github.com/arendst/Tasmota/releases">Firmware</a> with Web-Server enabled:<ul>
|
||||
<li>To backup or restore configurations from or to a Tasmota device you need a firmare with enabled web-server in admin mode (command <a href="https://github.com/arendst/Tasmota/wiki/Commands#wifi">WebServer 2</a>). This is the Tasmota default.</li>
|
||||
<li>If using your own compiled firmware be aware to enable the web-server (<code>#define USE_WEBSERVER</code> and <code>#define WEB_SERVER 2</code>).</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="file-types">File Types</h2>
|
||||
<p><em>decode-config.py</em> can handle the following backup file types: </p>
|
||||
<p><em>decode-config.py</em> can handle the following backup file types:</p>
|
||||
<h3 id="-dmp-format">.dmp Format</h3>
|
||||
<p>Configuration data as used by Tasmota "Backup/Restore Configuration" web interface.<br>This format is binary and encrypted.</p>
|
||||
<p>Configuration data as used by Tasmota "Backup/Restore Configuration" web interface.
|
||||
This format is binary and encrypted.</p>
|
||||
<h3 id="-json-format">.json Format</h3>
|
||||
<p>Configuration data in <a href="http://www.json.org/">JSON</a>-format.<br>This format is decrypted, human readable and editable and can also be used for the <code>--restore-file</code> parameter.<br>This file will be created by <em>decode-config.py</em> using the <code>--backup-file</code> with <code>--backup-type json</code> parameter, this is the default.</p>
|
||||
<p>Configuration data in <a href="http://www.json.org/">JSON</a>-format.
|
||||
This format is decrypted, human readable and editable and can also be used for the <code>--restore-file</code> parameter.
|
||||
This file will be created by <em>decode-config.py</em> using the <code>--backup-file</code> with <code>--backup-type json</code> parameter, this is the default.</p>
|
||||
<h3 id="-bin-format">.bin Format</h3>
|
||||
<p>Configuration data in binary format.<br>This format is binary decryptet, editable (e.g. using a hex editor) and can also be used for <code>--restore-file</code> command.<br>It will be created by <em>decode-config.py</em> using <code>--backup-file</code> with <code>--backup-type bin</code>.<br>Note:<br>The .bin file contains the same information as the original .dmp file from Tasmota "Backup/Restore Configuration" but it is decrpted and 4 byte longer than an original (it is a prefix header at the beginning). .bin file data starting at address 4 contains the same as the <strong>struct SYSCFG</strong> from Tasmota <a href="https://github.com/arendst/Tasmota/blob/master/sonoff/settings.h">settings.h</a> in decrypted format.</p>
|
||||
<p>Configuration data in binary format.
|
||||
This format is binary decryptet, editable (e.g. using a hex editor) and can also be used for <code>--restore-file</code> command.
|
||||
It will be created by <em>decode-config.py</em> using <code>--backup-file</code> with <code>--backup-type bin</code>.
|
||||
Note:
|
||||
The .bin file contains the same information as the original .dmp file from Tasmota "Backup/Restore Configuration" but it is decrpted and 4 byte longer than an original (it is a prefix header at the beginning). .bin file data starting at address 4 contains the same as the <strong>struct SYSCFG</strong> from Tasmota <a href="https://github.com/arendst/Tasmota/blob/master/tasmota/settings.h">settings.h</a> in decrypted format.</p>
|
||||
<h4 id="file-extensions">File extensions</h4>
|
||||
<p>You don't need to append exensions for your file name as <em>decode-config.py</em> uses auto extension as default. The extension will be choose based on file contents and <code>--backup-type</code> parameter.
|
||||
If you do not want using auto extensions use the <code>--no-extension</code> parameter.</p>
|
||||
|
@ -99,13 +115,13 @@ If you do not want using auto extensions use the <code>--no-extension</code> par
|
|||
<p>After download don't forget to set the executable flag under linux with <code>chmod +x decode-config.py</code> or call the program using <code>python decode-config.py...</code>.</p>
|
||||
<h3 id="basics">Basics</h3>
|
||||
<p>At least pass a source where you want to read the configuration data from using <code>-f <filename></code> or <code>-d <host></code>:</p>
|
||||
<p>The source can be either </p>
|
||||
<p>The source can be either</p>
|
||||
<ul>
|
||||
<li>a Tasmota device hostname or IP using the <code>-d <host></code> parameter</li>
|
||||
<li>a Tasmota <code>*.dmp</code> configuration file using <code>-f <filename></code> parameter</li>
|
||||
</ul>
|
||||
<p>Example: </p>
|
||||
<pre><code>decode-config<span class="hljs-selector-class">.py</span> -d sonoff-<span class="hljs-number">4281</span>
|
||||
<p>Example:</p>
|
||||
<pre><code>decode-config<span class="hljs-selector-class">.py</span> -d tasmota-<span class="hljs-number">4281</span>
|
||||
</code></pre><p>will output a human readable configuration in <a href="http://www.json.org/">JSON</a>-format:</p>
|
||||
<pre><code>{
|
||||
<span class="hljs-string">"altitude"</span>: <span class="hljs-number">112</span>,
|
||||
|
@ -120,24 +136,24 @@ If you do not want using auto extensions use the <code>--no-extension</code> par
|
|||
]
|
||||
}
|
||||
</code></pre><h3 id="save-backup-file">Save backup file</h3>
|
||||
<p>To save the output as backup file use <code>--backup-file <filename></code>, you can use placeholder for Version, Friendlyname and Hostname: </p>
|
||||
<pre><code><span class="hljs-selector-tag">decode-config</span><span class="hljs-selector-class">.py</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">sonoff-4281</span> <span class="hljs-selector-tag">--backup-file</span> <span class="hljs-selector-tag">Config_</span>@<span class="hljs-keyword">f_</span>@<span class="hljs-keyword">v</span>
|
||||
<p>To save the output as backup file use <code>--backup-file <filename></code>, you can use placeholder for Version, Friendlyname and Hostname:</p>
|
||||
<pre><code><span class="hljs-selector-tag">decode-config</span><span class="hljs-selector-class">.py</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">tasmota-4281</span> <span class="hljs-selector-tag">--backup-file</span> <span class="hljs-selector-tag">Config_</span>@<span class="hljs-keyword">f_</span>@<span class="hljs-keyword">v</span>
|
||||
</code></pre><p>If you have setup a WebPassword within Tasmota, use</p>
|
||||
<pre><code>decode-config<span class="hljs-selector-class">.py</span> -d sonoff-<span class="hljs-number">4281</span> -<span class="hljs-selector-tag">p</span> <yourpassword> --backup-file Config_@f_@v
|
||||
</code></pre><p>will create a file like <code>Config_Sonoff_6.4.0.json</code> (the part <code>Sonoff</code> and <code>6.4.0</code> will choosen related to your device configuration). Because the default backup file format is JSON, you can read and change it with any raw text editor.</p>
|
||||
<pre><code>decode-config<span class="hljs-selector-class">.py</span> -d tasmota-<span class="hljs-number">4281</span> -<span class="hljs-selector-tag">p</span> <yourpassword> --backup-file Config_@f_@v
|
||||
</code></pre><p>will create a file like <code>Config_Tasmota_6.4.0.json</code> (the part <code>Tasmota</code> and <code>6.4.0</code> will choosen related to your device configuration). Because the default backup file format is JSON, you can read and change it with any raw text editor.</p>
|
||||
<h3 id="restore-backup-file">Restore backup file</h3>
|
||||
<p>Reading back a saved (and possible changed) backup file use the <code>--restore-file <filename></code> parameter. This will read the (changed) configuration data from this file and send it back to the source device or filename.</p>
|
||||
<p>To restore the previously save backup file <code>Config_Sonoff_6.2.1.json</code> to device <code>sonoff-4281</code> use: </p>
|
||||
<pre><code><span class="hljs-selector-tag">decode-config</span><span class="hljs-selector-class">.py</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">sonoff-4281</span> <span class="hljs-selector-tag">--restore-file</span> <span class="hljs-selector-tag">Config_Sonoff_6</span><span class="hljs-selector-class">.2</span><span class="hljs-selector-class">.1</span><span class="hljs-selector-class">.json</span>
|
||||
<p>To restore the previously save backup file <code>Config_Tasmota_6.2.1.json</code> to device <code>tasmota-4281</code> use:</p>
|
||||
<pre><code><span class="hljs-selector-tag">decode-config</span><span class="hljs-selector-class">.py</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">tasmota-4281</span> <span class="hljs-selector-tag">--restore-file</span> <span class="hljs-selector-tag">Config_Tasmota_6</span><span class="hljs-selector-class">.2</span><span class="hljs-selector-class">.1</span><span class="hljs-selector-class">.json</span>
|
||||
</code></pre><p>with password set by WebPassword:</p>
|
||||
<pre><code><span class="hljs-selector-tag">decode-config</span><span class="hljs-selector-class">.py</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">sonoff-4281</span> <span class="hljs-selector-tag">-p</span> <<span class="hljs-selector-tag">yourpassword</span>> <span class="hljs-selector-tag">--restore-file</span> <span class="hljs-selector-tag">Config_Sonoff_6</span><span class="hljs-selector-class">.2</span><span class="hljs-selector-class">.1</span><span class="hljs-selector-class">.json</span>
|
||||
<pre><code><span class="hljs-selector-tag">decode-config</span><span class="hljs-selector-class">.py</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">tasmota-4281</span> <span class="hljs-selector-tag">-p</span> <<span class="hljs-selector-tag">yourpassword</span>> <span class="hljs-selector-tag">--restore-file</span> <span class="hljs-selector-tag">Config_Tasmota_6</span><span class="hljs-selector-class">.2</span><span class="hljs-selector-class">.1</span><span class="hljs-selector-class">.json</span>
|
||||
</code></pre><h3 id="output-to-screen">Output to screen</h3>
|
||||
<p>To force screen output use the <code>--output</code> parameter.</p>
|
||||
<p>Output to screen is default enabled when calling the program with a source parameter (-f or -d) but without any backup or restore parameter.</p>
|
||||
<h4 id="json-output">JSON output</h4>
|
||||
<p>The default output format is <a href="decode-config.md#-json-format">JSON</a>. You can force JSON output using the <code>--output-format json</code> parameter.</p>
|
||||
<p>Example:</p>
|
||||
<pre><code>decode-config.py -d sonoff<span class="hljs-number">-4281</span> -c my.conf -x <span class="hljs-symbol">Wifi</span> --output-format json
|
||||
<pre><code>decode-config.py -d tasmota<span class="hljs-number">-4281</span> -c my.conf -x <span class="hljs-symbol">Wifi</span> --output-format json
|
||||
|
||||
{
|
||||
...
|
||||
|
@ -171,7 +187,7 @@ If you do not want using auto extensions use the <code>--no-extension</code> par
|
|||
<h4 id="tasmota-command-output">Tasmota command output</h4>
|
||||
<p><em>decode-config.py</em> is able to translate the configuration data to (most all) Tasmota commands. To output your configuration as Tasmota commands use <code>--output-format cmnd</code> or <code>--output-format command</code>.</p>
|
||||
<p>Example:</p>
|
||||
<pre><code><span class="hljs-selector-tag">decode-config</span><span class="hljs-selector-class">.py</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">sonoff-4281</span> <span class="hljs-selector-tag">-c</span> <span class="hljs-selector-tag">my</span><span class="hljs-selector-class">.conf</span> <span class="hljs-selector-tag">-g</span> <span class="hljs-selector-tag">Wifi</span> <span class="hljs-selector-tag">--output-format</span> <span class="hljs-selector-tag">cmnd</span>
|
||||
<pre><code><span class="hljs-selector-tag">decode-config</span><span class="hljs-selector-class">.py</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">tasmota-4281</span> <span class="hljs-selector-tag">-c</span> <span class="hljs-selector-tag">my</span><span class="hljs-selector-class">.conf</span> <span class="hljs-selector-tag">-g</span> <span class="hljs-selector-tag">Wifi</span> <span class="hljs-selector-tag">--output-format</span> <span class="hljs-selector-tag">cmnd</span>
|
||||
|
||||
# <span class="hljs-selector-tag">Wifi</span>:
|
||||
<span class="hljs-selector-tag">AP</span> 0
|
||||
|
@ -192,11 +208,11 @@ If you do not want using auto extensions use the <code>--no-extension</code> par
|
|||
<span class="hljs-selector-tag">WifiConfig</span> 5
|
||||
</code></pre><p>Note: A few very specific module commands like MPC230xx, KNX and some Display commands are not supported. These are still available by JSON output.</p>
|
||||
<h3 id="filter-data">Filter data</h3>
|
||||
<p>The huge number of Tasmota configuration data can be overstrained and confusing, so the most of the configuration data are grouped into categories. </p>
|
||||
<p>With <em>decode-config.py</em> the following categories are available: <code>Display</code>, <code>Domoticz</code>, <code>Internal</code>, <code>KNX</code>, <code>Led</code>, <code>Logging</code>, <code>MCP230xx</code>, <code>MQTT</code>, <code>Main</code>, <code>Management</code>, <code>Pow</code>, <code>Sensor</code>, <code>Serial</code>, <code>SetOption</code>, <code>SonoffRF</code>, <code>System</code>, <code>Timers</code>, <code>Wifi</code></p>
|
||||
<p>The huge number of Tasmota configuration data can be overstrained and confusing, so the most of the configuration data are grouped into categories.</p>
|
||||
<p>With <em>decode-config.py</em> the following categories are available: <code>Display</code>, <code>Domoticz</code>, <code>Internal</code>, <code>KNX</code>, <code>Led</code>, <code>Logging</code>, <code>MCP230xx</code>, <code>MQTT</code>, <code>Main</code>, <code>Management</code>, <code>Pow</code>, <code>Sensor</code>, <code>Serial</code>, <code>SetOption</code>, <code>RF</code>, <code>System</code>, <code>Timers</code>, <code>Wifi</code></p>
|
||||
<p>These are similary to the categories on <a href="Tasmota Command Wiki">https://github.com/arendst/Tasmota/wiki/Commands</a>.</p>
|
||||
<p>To filter outputs to a subset of groups use the <code>-g</code> or <code>--group</code> arg concatenating the grooup you want, e. g.</p>
|
||||
<pre><code>decode-config<span class="hljs-selector-class">.py</span> -d sonoff-<span class="hljs-number">4281</span> -c my<span class="hljs-selector-class">.conf</span> --output-format cmnd --group Main MQTT Management Wifi
|
||||
<pre><code>decode-config<span class="hljs-selector-class">.py</span> -d tasmota-<span class="hljs-number">4281</span> -c my<span class="hljs-selector-class">.conf</span> --output-format cmnd --group Main MQTT Management Wifi
|
||||
</code></pre><h3 id="configuration-file">Configuration file</h3>
|
||||
<p>Each argument that start with <code>--</code> (eg. <code>--file</code>) 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 <a href="https://pypi.org/project/ConfigArgParse/">https://pypi.org/project/ConfigArgParse</a>).</p>
|
||||
<p>If an argument is specified in more than one place, then commandline values override config file values which override defaults. This is usefull if you always use the same argument or a basic set of arguments.</p>
|
||||
|
@ -206,7 +222,7 @@ If you do not want using auto extensions use the <code>--no-extension</code> par
|
|||
<span class="hljs-attr">username</span> = admin
|
||||
<span class="hljs-attr">password</span> = myPaszxwo!z
|
||||
</code></pre><p>To make a backup file from example above you can now pass the config file instead using the password on command line:</p>
|
||||
<pre><code><span class="hljs-selector-tag">decode-config</span><span class="hljs-selector-class">.py</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">sonoff-4281</span> <span class="hljs-selector-tag">-c</span> <span class="hljs-selector-tag">my</span><span class="hljs-selector-class">.conf</span> <span class="hljs-selector-tag">--backup-file</span> <span class="hljs-selector-tag">Config_</span>@<span class="hljs-keyword">f_</span>@<span class="hljs-keyword">v</span>
|
||||
<pre><code><span class="hljs-selector-tag">decode-config</span><span class="hljs-selector-class">.py</span> <span class="hljs-selector-tag">-d</span> <span class="hljs-selector-tag">tasmota-4281</span> <span class="hljs-selector-tag">-c</span> <span class="hljs-selector-tag">my</span><span class="hljs-selector-class">.conf</span> <span class="hljs-selector-tag">--backup-file</span> <span class="hljs-selector-tag">Config_</span>@<span class="hljs-keyword">f_</span>@<span class="hljs-keyword">v</span>
|
||||
</code></pre><h3 id="more-program-arguments">More program arguments</h3>
|
||||
<p>For better reading each short written arg (minus sign <code>-</code>) has a corresponding long version (two minus signs <code>--</code>), eg. <code>--device</code> for <code>-d</code> or <code>--file</code> for <code>-f</code> (note: not even all <code>--</code> arg has a corresponding <code>-</code> one).</p>
|
||||
<p>A short list of possible program args is displayed using <code>-h</code> or <code>--help</code>.</p>
|
||||
|
@ -219,7 +235,7 @@ If you do not want using auto extensions use the <code>--no-extension</code> par
|
|||
[--cmnd-indent <indent>] [--cmnd-groups]
|
||||
[--cmnd-nogroups] [--cmnd-<span class="hljs-keyword">sort</span>] [--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} ...]]
|
||||
[-<span class="hljs-keyword">g</span> {Control,Devices,<span class="hljs-keyword">Display</span>,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rf,Rules,Sensor,Serial,Setoption,Shutter,System,<span class="hljs-keyword">Timer</span>,Wifi} [{Control,Devices,<span class="hljs-keyword">Display</span>,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rf,Rules,Sensor,Serial,Setoption,Shutter,System,<span class="hljs-keyword">Timer</span>,Wifi} ...]]
|
||||
[--ignore-warnings] [-<span class="hljs-keyword">h</span>] [-<span class="hljs-keyword">H</span>] [-v] [-V]
|
||||
|
||||
Backup/<span class="hljs-keyword">Restore</span> Tasmota configuration data. <span class="hljs-keyword">Args</span> that start with '--'
|
||||
|
@ -299,7 +315,7 @@ Common:
|
|||
(default <span class="hljs-keyword">do</span> not output <span class="hljs-keyword">on</span> backup or <span class="hljs-keyword">restore</span> usage)
|
||||
-T, --output-<span class="hljs-keyword">format</span> json|cmnd|command
|
||||
<span class="hljs-keyword">display</span> output <span class="hljs-keyword">format</span> (default: 'json')
|
||||
-g, --group {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi}
|
||||
-<span class="hljs-keyword">g</span>, --group {Control,Devices,<span class="hljs-keyword">Display</span>,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rf,Rules,Sensor,Serial,Setoption,Shutter,System,<span class="hljs-keyword">Timer</span>,Wifi}
|
||||
limit data processing to command groups (default <span class="hljs-keyword">no</span>
|
||||
filter)
|
||||
--ignore-warnings <span class="hljs-keyword">do</span> not <span class="hljs-keyword">exit</span> <span class="hljs-keyword">on</span> warnings. Not recommended, used <span class="hljs-keyword">by</span> your
|
||||
|
@ -331,26 +347,29 @@ json-indent <span class="hljs-number">2</span>
|
|||
</code></pre><h4 id="using-tasmota-binary-configuration-files">Using Tasmota binary configuration files</h4>
|
||||
<ol>
|
||||
<li><p>Restore a Tasmota configuration file</p>
|
||||
<p> <code>decode-config.py -c my.conf -d sonoff --restore-file Config_Sonoff_6.2.1.dmp</code></p>
|
||||
<p> <code>decode-config.py -c my.conf -d tasmota --restore-file Config_Tasmota_6.2.1.dmp</code></p>
|
||||
</li>
|
||||
<li><p>Backup device using Tasmota configuration compatible format</p>
|
||||
<p>a) use file extension to choice the file format</p>
|
||||
<p> <code>decode-config.py -c my.conf -d sonoff --backup-file Config_@f_@v.dmp</code></p>
|
||||
<p> <code>decode-config.py -c my.conf -d tasmota --backup-file Config_@f_@v.dmp</code></p>
|
||||
<p>b) use args to choice the file format</p>
|
||||
<p> <code>decode-config.py -c my.conf -d sonoff --backup-type dmp --backup-file Config_@f_@v</code></p>
|
||||
<p> <code>decode-config.py -c my.conf -d tasmota --backup-type dmp --backup-file Config_@f_@v</code></p>
|
||||
</li>
|
||||
</ol>
|
||||
<h4 id="use-batch-processing">Use batch processing</h4>
|
||||
<pre><code><span class="hljs-keyword">for</span> device <span class="hljs-keyword">in</span> sonoff1 sonoff2 sonoff3; <span class="hljs-keyword">do</span> ./decode-config.py -c my.conf -d <span class="hljs-variable">$device</span> -o Config<span class="hljs-number">_</span><span class="hljs-variable">@f_</span><span class="hljs-variable">@v</span>
|
||||
<pre><code><span class="hljs-keyword">for</span> device <span class="hljs-keyword">in</span> tasmota1 tasmota2 tasmota3; <span class="hljs-keyword">do</span> ./decode-config.py -c my.conf -d <span class="hljs-variable">$device</span> -o Config<span class="hljs-number">_</span><span class="hljs-variable">@f_</span><span class="hljs-variable">@v</span>
|
||||
</code></pre><p>or under windows</p>
|
||||
<pre><code><span class="hljs-keyword">for</span> device <span class="hljs-keyword">in</span> (sonoff1 sonoff2 sonoff3) <span class="hljs-keyword">do</span> <span class="hljs-keyword">python</span> decode-config.py -c my.conf -d %device -o Config_@f_@v
|
||||
</code></pre><p>will produce JSON configuration files for host sonoff1, sonoff2 and sonoff3 using friendly name and Tasmota firmware version for backup filenames.</p>
|
||||
<pre><code><span class="hljs-keyword">for</span> device <span class="hljs-keyword">in</span> (tasmota1 tasmota2 tasmota3) <span class="hljs-keyword">do</span> <span class="hljs-keyword">python</span> decode-config.py -c my.conf -d %device -o Config_@f_@v
|
||||
</code></pre><p>will produce JSON configuration files for host tasmota1, tasmota2 and tasmota3 using friendly name and Tasmota firmware version for backup filenames.</p>
|
||||
<h2 id="notes">Notes</h2>
|
||||
<p>Some general notes:</p>
|
||||
<ul>
|
||||
<li>Filename replacement macros <strong>@h</strong> and <strong>@H</strong>:<ul>
|
||||
<li><strong>@h</strong><br>The <strong>@h</strong> replacement macro uses the hostname configured with the Tasomta Wifi <code>Hostname <host></code> command (defaults to <code>%s-%04d</code>). It will not use the network hostname of your device because this is not available when working with files only (e.g. <code>--file <filename></code> as source).<br>To prevent having a useless % in your filename, <strong>@h</strong> will not replaced by configuration data hostname if this contains '%' characters.</li>
|
||||
<li><strong>@H</strong><br>If you want to use the network hostname within your filename, use the <strong>@H</strong> replacement macro instead - but be aware this will only replaced if you are using a network device as source (<code>-d</code>, <code>--device</code>, <code>--host</code>); it will not work when using a file as source (<code>-f</code>, <code>--file</code>)</li>
|
||||
<li><strong>@h</strong>
|
||||
The <strong>@h</strong> replacement macro uses the hostname configured with the Tasomta Wifi <code>Hostname <host></code> command (defaults to <code>%s-%04d</code>). It will not use the network hostname of your device because this is not available when working with files only (e.g. <code>--file <filename></code> as source).
|
||||
To prevent having a useless % in your filename, <strong>@h</strong> will not replaced by configuration data hostname if this contains '%' characters.</li>
|
||||
<li><strong>@H</strong>
|
||||
If you want to use the network hostname within your filename, use the <strong>@H</strong> replacement macro instead - but be aware this will only replaced if you are using a network device as source (<code>-d</code>, <code>--device</code>, <code>--host</code>); it will not work when using a file as source (<code>-f</code>, <code>--file</code>)</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -211,7 +211,7 @@ Note: A few very specific module commands like MPC230xx, KNX and some Display co
|
|||
### Filter data
|
||||
The huge number of Tasmota configuration data can be overstrained and confusing, so the most of the configuration data are grouped into categories.
|
||||
|
||||
With _decode-config.py_ the following categories are available: `Display`, `Domoticz`, `Internal`, `KNX`, `Led`, `Logging`, `MCP230xx`, `MQTT`, `Main`, `Management`, `Pow`, `Sensor`, `Serial`, `SetOption`, `SonoffRF`, `System`, `Timers`, `Wifi`
|
||||
With _decode-config.py_ the following categories are available: `Display`, `Domoticz`, `Internal`, `KNX`, `Led`, `Logging`, `MCP230xx`, `MQTT`, `Main`, `Management`, `Pow`, `Sensor`, `Serial`, `SetOption`, `RF`, `System`, `Timers`, `Wifi`
|
||||
|
||||
These are similary to the categories on [https://github.com/arendst/Tasmota/wiki/Commands](Tasmota Command Wiki).
|
||||
|
||||
|
@ -254,7 +254,7 @@ For advanced help use `-H` or `--full-help`:
|
|||
[--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,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi} ...]]
|
||||
[-g {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rf,Rules,Sensor,Serial,Setoption,Shutter,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rf,Rules,Sensor,Serial,Setoption,Shutter,System,Timer,Wifi} ...]]
|
||||
[--ignore-warnings] [-h] [-H] [-v] [-V]
|
||||
|
||||
Backup/Restore Tasmota configuration data. Args that start with '--'
|
||||
|
@ -334,7 +334,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 {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi}
|
||||
-g, --group {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rf,Rules,Sensor,Serial,Setoption,Shutter,System,Timer,Wifi}
|
||||
limit data processing to command groups (default no
|
||||
filter)
|
||||
--ignore-warnings do not exit on warnings. Not recommended, used by your
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
VER = '2.3.0036'
|
||||
from __future__ import print_function
|
||||
VER = '2.4.0037'
|
||||
|
||||
"""
|
||||
decode-config.py - Backup/Restore Tasmota configuration data
|
||||
|
@ -43,7 +44,7 @@ Usage: decode-config.py [-f <filename>] [-d <host>] [-P <port>]
|
|||
[--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,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi} ...]]
|
||||
[-g {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Rf,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Rf,System,Timer,Wifi} ...]]
|
||||
[--ignore-warnings] [-h] [-H] [-v] [-V]
|
||||
|
||||
Backup/Restore Tasmota configuration data. Args that start with '--'
|
||||
|
@ -123,7 +124,7 @@ Usage: decode-config.py [-f <filename>] [-d <host>] [-P <port>]
|
|||
(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,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi}
|
||||
-g, --group {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Rf,System,Timer,Wifi}
|
||||
limit data processing to command groups (default no
|
||||
filter)
|
||||
--ignore-warnings do not exit on warnings. Not recommended, used by your
|
||||
|
@ -184,7 +185,7 @@ import io
|
|||
import sys, platform
|
||||
def ModuleImportError(module):
|
||||
er = str(module)
|
||||
print >> sys.stderr, "{}. Try 'pip install {}' to install it".format(er,er.split(' ')[len(er.split(' '))-1])
|
||||
print('{}, try "pip install {}"'.format(er,er.split(' ')[len(er.split(' '))-1]), file=sys.stderr)
|
||||
sys.exit(ExitCode.MODULE_NOT_FOUND)
|
||||
try:
|
||||
from datetime import datetime
|
||||
|
@ -198,8 +199,11 @@ try:
|
|||
import json
|
||||
import configargparse
|
||||
import pycurl
|
||||
import urllib2
|
||||
except ImportError, e:
|
||||
if sys.version_info.major==2:
|
||||
import urllib2
|
||||
else:
|
||||
import urllib
|
||||
except ImportError as e:
|
||||
ModuleImportError(e)
|
||||
|
||||
# ======================================================================
|
||||
|
@ -276,7 +280,7 @@ Settings dictionary describes the config file fields definition:
|
|||
defines the use of data at <addrdef>
|
||||
format is defined in 'struct module format string'
|
||||
see
|
||||
https://docs.python.org/2.7/library/struct.html#format-strings
|
||||
https://docs.python.org/3.8/library/struct.html#format-strings
|
||||
<setting>: <setting>
|
||||
A dictionary describes a (sub)setting dictonary
|
||||
and can recursively define another <setting>
|
||||
|
@ -556,7 +560,7 @@ Setting_5_10_0 = {
|
|||
'pulse_counter_type4': ('<H', (0x5D0,1,3), (None, None, ('Sensor', '"CounterType4 {}".format($)')) ),
|
||||
}, 0x5D0, (None, None, ('Sensor', None)), (None, None) ),
|
||||
'pulse_counter_debounce': ('<H', 0x5D2, (None, '0 <= $ <= 3200', ('Sensor', '"CounterDebounce {}".format($)')) ),
|
||||
'rf_code': ('B', 0x5D4, ([17,9],None, ('SonoffRF', None)), '"0x{:02x}".format($)'),
|
||||
'rf_code': ('B', 0x5D4, ([17,9],None, ('Rf', None)), '"0x{:02x}".format($)'),
|
||||
}
|
||||
# ======================================================================
|
||||
Setting_5_11_0 = copy.deepcopy(Setting_5_10_0)
|
||||
|
@ -1043,8 +1047,8 @@ Setting_6_6_0_14.update ({
|
|||
'mqttlog_level': ('B', 0x1E7, (None, None, ('Management', '"MqttLog {}".format($)')) ),
|
||||
'pcf8574_config': ('B', 0xE88, ([8], None, ('Devices', None)) ),
|
||||
'shutter_accuracy': ('B', 0x1E6, (None, None, ('Shutter', None)) ),
|
||||
'shutter_opentime': ('<H', 0xE40, ([4], None, ('Shutter', '"ShutterOpenDuration{} {:.1f}".format(#,$/10)')) ),
|
||||
'shutter_closetime': ('<H', 0xE48, ([4], None, ('Shutter', '"ShutterCloseDuration{} {:.1f}".format(#,$/10)')) ),
|
||||
'shutter_opentime': ('<H', 0xE40, ([4], None, ('Shutter', '"ShutterOpenDuration{} {:.1f}".format(#,$/10.0)')) ),
|
||||
'shutter_closetime': ('<H', 0xE48, ([4], None, ('Shutter', '"ShutterCloseDuration{} {:.1f}".format(#,$/10.0)')) ),
|
||||
'shuttercoeff': ('<H', 0xE50, ([5,4],None, ('Shutter', None)) ),
|
||||
'shutter_invert': ('B', 0xE78, ([4], None, ('Shutter', '"ShutterInvert{} {}".format(#,$)')) ),
|
||||
'shutter_set50percent': ('B', 0xE7C, ([4], None, ('Shutter', '"ShutterSetHalfway{} {}".format(#,$)')) ),
|
||||
|
@ -1081,7 +1085,55 @@ Setting_6_6_0_20.update ({
|
|||
'energy_power_delta': ('<H', 0xE98, (None, '0 <= $ < 32000', ('Power', '"PowerDelta {}".format($)')) ),
|
||||
})
|
||||
# ======================================================================
|
||||
Setting_6_6_0_21 = copy.deepcopy(Setting_6_6_0_20)
|
||||
Setting_6_6_0_21['flag'][0].pop('value_units',None)
|
||||
Setting_6_6_0_21['flag3'][0].pop('tuya_dimmer_range_255',None)
|
||||
Setting_6_6_0_21['flag3'][0].update ({
|
||||
'tuya_serial_mqtt_publish': ('<L', (0x3A0,1,16), (None, None, ('SetOption', '"SetOption66 {}".format($)')) ),
|
||||
})
|
||||
# ======================================================================
|
||||
Setting_7_0_0_1 = copy.deepcopy(Setting_6_6_0_21)
|
||||
Setting_7_0_0_1.pop('register8',None)
|
||||
Setting_7_0_0_1.update ({
|
||||
'shutter_motordelay': ('B', 0xE9A, ([4], None, ('Shutter', '"ShutterMotorDelay{} {:.1f}".format(#,$/20.0)')) ),
|
||||
'flag4': ('<L', 0x1E0, (None, None, ('System', None)), '"0x{:08x}".format($)' ),
|
||||
})
|
||||
Setting_7_0_0_1['flag3'][0].update ({
|
||||
'cors_enabled': ('<L', (0x3A0,1,23), (None, None, ('SetOption', '"SetOption73 {}".format($)')) ),
|
||||
'ds18x20_internal_pullup': ('<L', (0x3A0,1,24), (None, None, ('SetOption', '"SetOption74 {}".format($)')) ),
|
||||
'grouptopic_mode': ('<L', (0x3A0,1,25), (None, None, ('SetOption', '"SetOption75 {}".format($)')) ),
|
||||
})
|
||||
# ======================================================================
|
||||
Setting_7_0_0_2 = copy.deepcopy(Setting_7_0_0_1)
|
||||
Setting_7_0_0_2.update ({
|
||||
'web_color2': ('3B', 0xEA0, ([1], None, ('Wifi', '"WebColor{} {}{:06x}".format(#+18,chr(35),int($,0))')), '"0x{:06x}".format($)' ),
|
||||
})
|
||||
# ======================================================================
|
||||
Setting_7_0_0_3 = copy.deepcopy(Setting_7_0_0_2)
|
||||
Setting_7_0_0_3.update ({
|
||||
'i2c_drivers': ('<L', 0xFEC, ([3], None, ('Management', None)),'"0x{:08x}".format($)' ),
|
||||
})
|
||||
# ======================================================================
|
||||
Setting_7_0_0_4 = copy.deepcopy(Setting_7_0_0_3)
|
||||
Setting_7_0_0_4.update ({
|
||||
'wifi_output_power': ('B', 0x1E5, (None, None, ('Wifi', '"WifiPower {:.1f}".format($/10.0)')) ),
|
||||
})
|
||||
Setting_7_0_0_4['flag3'][0].update ({
|
||||
'bootcount_update': ('<L', (0x3A0,1,26), (None, None, ('SetOption', '"SetOption76 {}".format($)')) ),
|
||||
})
|
||||
# ======================================================================
|
||||
Setting_7_0_0_5 = copy.deepcopy(Setting_7_0_0_4)
|
||||
Setting_7_0_0_5.update ({
|
||||
'temp_comp': ('b', 0xE9E, (None, None, ('Sensor', '"TempOffset {:.1f}".format($/10.0)')) ),
|
||||
})
|
||||
# ======================================================================
|
||||
Settings = [
|
||||
(0x7000005,0x1000, Setting_7_0_0_5),
|
||||
(0x7000004,0x1000, Setting_7_0_0_4),
|
||||
(0x7000003,0x1000, Setting_7_0_0_3),
|
||||
(0x7000002,0x1000, Setting_7_0_0_2),
|
||||
(0x7000001,0x1000, Setting_7_0_0_1),
|
||||
(0x6060015,0x1000, Setting_6_6_0_21),
|
||||
(0x6060014,0x1000, Setting_6_6_0_20),
|
||||
(0x6060012,0x1000, Setting_6_6_0_18),
|
||||
(0x606000F,0x1000, Setting_6_6_0_15),
|
||||
|
@ -1162,13 +1214,14 @@ def message(msg, type_=None, status=None, line=None):
|
|||
@param status:
|
||||
status number
|
||||
"""
|
||||
print >> sys.stderr, '{styp}{sdelimiter}{sstatus}{slineno}{scolon}{smgs}'.format(\
|
||||
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 type_ is not None or line is not None else '',
|
||||
smgs=msg,
|
||||
slineno=' (@{:04d})'.format(line) if line is not None else '')
|
||||
print('{styp}{sdelimiter}{sstatus}{slineno}{scolon}{smgs}'.format(\
|
||||
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 type_ is not None or line is not None else '',
|
||||
smgs=msg,
|
||||
slineno=' (@{:04d})'.format(line) if line is not None else '')
|
||||
, file=sys.stderr)
|
||||
|
||||
|
||||
def exit(status=0, msg="end", type_=LogType.ERROR, src=None, doexit=True, line=None):
|
||||
|
@ -1200,11 +1253,11 @@ def ShortHelp(doexit=True):
|
|||
@param doexit:
|
||||
sys.exit with OK if True
|
||||
"""
|
||||
print parser.description
|
||||
print(parser.description)
|
||||
print
|
||||
parser.print_usage()
|
||||
print
|
||||
print "For advanced help use '{prog} -H' or '{prog} --full-help'".format(prog=os.path.basename(sys.argv[0]))
|
||||
print("For advanced help use '{prog} -H' or '{prog} --full-help'".format(prog=os.path.basename(sys.argv[0])))
|
||||
if doexit:
|
||||
sys.exit(ExitCode.OK)
|
||||
|
||||
|
@ -1554,8 +1607,8 @@ def LoadTasmotaConfig(filename):
|
|||
try:
|
||||
with open(filename, "rb") as tasmotafile:
|
||||
encode_cfg = tasmotafile.read()
|
||||
except Exception, e:
|
||||
exit(e[0], "'{}' {}".format(filename, e[1]),line=inspect.getlineno(inspect.currentframe()))
|
||||
except Exception as e:
|
||||
exit(e.args[0], "'{}' {}".format(filename, e[1]),line=inspect.getlineno(inspect.currentframe()))
|
||||
|
||||
return encode_cfg
|
||||
|
||||
|
@ -1597,8 +1650,8 @@ def TasmotaGet(cmnd, host, port, username=DEFAULTS['source']['username'], passwo
|
|||
c.perform()
|
||||
responsecode = c.getinfo(c.RESPONSE_CODE)
|
||||
response = header.response()
|
||||
except Exception, e:
|
||||
exit(e[0], e[1],line=inspect.getlineno(inspect.currentframe()))
|
||||
except Exception as e:
|
||||
exit(e.args[0], e.args[1], line=inspect.getlineno(inspect.currentframe()))
|
||||
finally:
|
||||
c.close()
|
||||
|
||||
|
@ -1727,8 +1780,8 @@ def PushTasmotaConfig(encode_cfg, host, port, username=DEFAULTS['source']['usern
|
|||
try:
|
||||
c.perform()
|
||||
responsecode = c.getinfo(c.RESPONSE_CODE)
|
||||
except Exception, e:
|
||||
return e[0], e[1]
|
||||
except Exception as e:
|
||||
return e.args[0], e.args[1]
|
||||
|
||||
c.close()
|
||||
|
||||
|
@ -1839,7 +1892,7 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
|
|||
|
||||
# calling with nothing is wrong
|
||||
if fielddef is None:
|
||||
print >> sys.stderr, '<fielddef> is None'
|
||||
print('<fielddef> is None', file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
|
||||
# get top level items
|
||||
|
@ -1850,7 +1903,7 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
|
|||
# converter present
|
||||
format_, addrdef, datadef, converter = fielddef
|
||||
else:
|
||||
print >> sys.stderr, 'wrong <fielddef> {} length ({}) in setting'.format(fielddef, len(fielddef))
|
||||
print('wrong <fielddef> {} length ({}) in setting'.format(fielddef, len(fielddef)), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
|
||||
# ignore calls with 'root' setting
|
||||
|
@ -1858,7 +1911,7 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
|
|||
return eval(fields)
|
||||
|
||||
if not isinstance(format_, (unicode,str,dict)):
|
||||
print >> sys.stderr, 'wrong <format> {} type {} in <fielddef> {}'.format(format_, type(format_), fielddef)
|
||||
print('wrong <format> {} type {} in <fielddef> {}'.format(format_, type(format_), fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
|
||||
# extract addrdef items
|
||||
|
@ -1868,16 +1921,16 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
|
|||
# baseaddr bit definition
|
||||
baseaddr, bits, bitshift = baseaddr
|
||||
if not isinstance(bits, int):
|
||||
print >> sys.stderr, '<bits> must be defined as integer in <fielddef> {}'.format(bits, fielddef)
|
||||
print('<bits> must be defined as integer in <fielddef> {}'.format(bits, fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
if not isinstance(bitshift, int):
|
||||
print >> sys.stderr, '<bitshift> must be defined as integer in <fielddef> {}'.format(bitshift, fielddef)
|
||||
print('<bitshift> must be defined as integer in <fielddef> {}'.format(bitshift, fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
else:
|
||||
print >> sys.stderr, 'wrong <addrdef> {} length ({}) in <fielddef> {}'.format(addrdef, len(addrdef), fielddef)
|
||||
print('wrong <addrdef> {} length ({}) in <fielddef> {}'.format(addrdef, len(addrdef), fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
if not isinstance(baseaddr, int):
|
||||
print >> sys.stderr, '<baseaddr> must be defined as integer in <fielddef> {}'.format(baseaddr, fielddef)
|
||||
print('<baseaddr> must be defined as integer in <fielddef> {}'.format(baseaddr, fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
|
||||
# extract datadef items
|
||||
|
@ -1893,27 +1946,27 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
|
|||
if isinstance(cmd, (tuple)) and len(cmd) == 2:
|
||||
group, tasmotacmnd = cmd
|
||||
if group is not None and not isinstance(group, (str, unicode)):
|
||||
print >> sys.stderr, 'wrong <group> {} in <fielddef> {}'.format(group, fielddef)
|
||||
print('wrong <group> {} in <fielddef> {}'.format(group, fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> 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 <tasmotacmnd> {} in <fielddef> {}'.format(tasmotacmnd, fielddef)
|
||||
print('wrong <tasmotacmnd> {} in <fielddef> {}'.format(tasmotacmnd, fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
else:
|
||||
if tasmotacmnd is not None and not callable(tasmotacmnd) and not isinstance(tasmotacmnd, (str, unicode)):
|
||||
print >> sys.stderr, 'wrong <tasmotacmnd> {} in <fielddef> {}'.format(tasmotacmnd, fielddef)
|
||||
print('wrong <tasmotacmnd> {} in <fielddef> {}'.format(tasmotacmnd, fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
else:
|
||||
print >> sys.stderr, 'wrong <cmd> {} length ({}) in <fielddef> {}'.format(cmd, len(cmd), fielddef)
|
||||
print('wrong <cmd> {} length ({}) in <fielddef> {}'.format(cmd, len(cmd), fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
else:
|
||||
print >> sys.stderr, 'wrong <datadef> {} length ({}) in <fielddef> {}'.format(datadef, len(datadef), fielddef)
|
||||
print('wrong <datadef> {} length ({}) in <fielddef> {}'.format(datadef, len(datadef), fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
|
||||
if validate is not None and (not isinstance(validate, (unicode,str)) and not callable(validate)):
|
||||
print >> sys.stderr, 'wrong <validate> {} type {} in <fielddef> {}'.format(validate, type(validate), fielddef)
|
||||
print('wrong <validate> {} type {} in <fielddef> {}'.format(validate, type(validate), fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
|
||||
# convert single int into one-dimensional list
|
||||
|
@ -1921,7 +1974,7 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
|
|||
arraydef = [arraydef]
|
||||
|
||||
if arraydef is not None and not isinstance(arraydef, (list)):
|
||||
print >> sys.stderr, 'wrong <arraydef> {} type {} in <fielddef> {}'.format(arraydef, type(arraydef), fielddef)
|
||||
print('wrong <arraydef> {} type {} in <fielddef> {}'.format(arraydef, type(arraydef), fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
|
||||
# get read/write converter items
|
||||
|
@ -1931,13 +1984,13 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
|
|||
# converter has read/write converter
|
||||
readconverter, writeconverter = converter
|
||||
if readconverter is not None and not isinstance(readconverter, (str,unicode)) and not callable(readconverter):
|
||||
print >> sys.stderr, 'wrong <readconverter> {} type {} in <fielddef> {}'.format(readconverter, type(readconverter), fielddef)
|
||||
print('wrong <readconverter> {} type {} in <fielddef> {}'.format(readconverter, type(readconverter), fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
if writeconverter is not None and (not isinstance(writeconverter, (bool,str,unicode)) and not callable(writeconverter)):
|
||||
print >> sys.stderr, 'wrong <writeconverter> {} type {} in <fielddef> {}'.format(writeconverter, type(writeconverter), fielddef)
|
||||
print('wrong <writeconverter> {} type {} in <fielddef> {}'.format(writeconverter, type(writeconverter), fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
else:
|
||||
print >> sys.stderr, 'wrong <converter> {} length ({}) in <fielddef> {}'.format(converter, len(converter), fielddef)
|
||||
print('wrong <converter> {} length ({}) in <fielddef> {}'.format(converter, len(converter), fielddef), file=sys.stderr)
|
||||
raise SyntaxError('<fielddef> error')
|
||||
|
||||
|
||||
|
@ -1975,8 +2028,8 @@ def ReadWriteConverter(value, fielddef, read=True, raw=False):
|
|||
return eval(conv.replace('$','value'))
|
||||
elif callable(conv): # use as format function
|
||||
return conv(value)
|
||||
except Exception, e:
|
||||
exit(e[0], e[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe()))
|
||||
except Exception as e:
|
||||
exit(e.args[0], e.args[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe()))
|
||||
|
||||
return value
|
||||
|
||||
|
@ -2294,7 +2347,7 @@ def SetFieldValue(fielddef, dobj, addr, value):
|
|||
formatcnt = GetFormatCount(format_)
|
||||
singletype, bitsize = GetFormatType(format_)
|
||||
if args.debug >= 2:
|
||||
print >> sys.stderr, "SetFieldValue(): fielddef {}, addr 0x{:04x} value {} formatcnt {} singletype {} bitsize {} ".format(fielddef,addr,value,formatcnt,singletype,bitsize)
|
||||
print("SetFieldValue(): fielddef {}, addr 0x{:04x} value {} formatcnt {} singletype {} bitsize {} ".format(fielddef,addr,value,formatcnt,singletype,bitsize), file=sys.stderr)
|
||||
if not format_[-1:].lower() in ['s','p']:
|
||||
addr += (bitsize / 8) * formatcnt
|
||||
for _ in range(0, formatcnt):
|
||||
|
@ -2305,7 +2358,7 @@ def SetFieldValue(fielddef, dobj, addr, value):
|
|||
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)
|
||||
print("SetFieldValue(): Single type - fielddef {}, addr 0x{:04x} value {} singletype {} bitsize {}".format(fielddef,addr,val,singletype,bitsize), file=sys.stderr)
|
||||
try:
|
||||
struct.pack_into(singletype, dobj, addr, val)
|
||||
except struct.error as e:
|
||||
|
@ -2317,7 +2370,7 @@ def SetFieldValue(fielddef, dobj, addr, value):
|
|||
value >>= bitsize
|
||||
else:
|
||||
if args.debug >= 3:
|
||||
print >> sys.stderr, "SetFieldValue(): String type - fielddef {}, addr 0x{:04x} value {} format_ {}".format(fielddef,addr,value,format_)
|
||||
print("SetFieldValue(): String type - fielddef {}, addr 0x{:04x} value {} format_ {}".format(fielddef,addr,value,format_), file=sys.stderr)
|
||||
try:
|
||||
struct.pack_into(format_, dobj, addr, value)
|
||||
except struct.error as e:
|
||||
|
@ -2427,7 +2480,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""):
|
|||
# do not write readonly values
|
||||
if writeconverter is False:
|
||||
if args.debug >= 2:
|
||||
print >> sys.stderr, "SetField(): Readonly '{}' using '{}'/{}{} @{} skipped".format(fieldname, format_, arraydef, bits, hex(baseaddr+addroffset))
|
||||
print("SetField(): Readonly '{}' using '{}'/{}{} @{} skipped".format(fieldname, format_, arraydef, bits, hex(baseaddr+addroffset)), file=sys.stderr)
|
||||
return dobj
|
||||
|
||||
# <arraydef> contains a list
|
||||
|
@ -2465,16 +2518,16 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""):
|
|||
if format_[-1:] in ['c']:
|
||||
try:
|
||||
value = ReadWriteConverter(restore.encode(STR_ENCODING)[0], fielddef, read=False)
|
||||
except Exception, e:
|
||||
exit(e[0], e[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe()))
|
||||
except Exception as e:
|
||||
exit(e.args[0], e.args[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe()))
|
||||
valid = False
|
||||
|
||||
# bool
|
||||
elif format_[-1:] in ['?']:
|
||||
try:
|
||||
value = ReadWriteConverter(bool(restore), fielddef, read=False)
|
||||
except Exception, e:
|
||||
exit(e[0], e[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe()))
|
||||
except Exception as e:
|
||||
exit(e.args[0], e.args[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe()))
|
||||
valid = False
|
||||
|
||||
# integer
|
||||
|
@ -2560,7 +2613,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""):
|
|||
if args.debug >= 2:
|
||||
sbits = " {} bits shift {}".format(bits, bitshift) if bits else ""
|
||||
strvalue = "{} [{}]".format(_value, hex(value)) if isinstance(_value, int) else _value
|
||||
print >> sys.stderr, "SetField(): Set '{}' using '{}'/{}{} @{} to {}".format(fieldname, format_, arraydef, sbits, hex(baseaddr+addroffset), strvalue)
|
||||
print("SetField(): Set '{}' using '{}'/{}{} @{} to {}".format(fieldname, format_, arraydef, sbits, hex(baseaddr+addroffset), strvalue), file=sys.stderr)
|
||||
if fieldname != 'cfg_crc' and fieldname != '_':
|
||||
prevvalue = GetFieldValue(fielddef, dobj, baseaddr+addroffset)
|
||||
dobj = SetFieldValue(fielddef, dobj, baseaddr+addroffset, value)
|
||||
|
@ -2569,7 +2622,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""):
|
|||
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))
|
||||
print("SetField(): Special field '{}' using '{}'/{}{} @{} skipped".format(fieldname, format_, arraydef, bits, hex(baseaddr+addroffset)), file=sys.stderr)
|
||||
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_), type_=LogType.WARNING, doexit=not args.ignorewarning)
|
||||
|
@ -2871,8 +2924,8 @@ def Backup(backupfile, backupfileformat, encode_cfg, decode_cfg, configmapping):
|
|||
try:
|
||||
with open(backup_filename, "wb") as backupfp:
|
||||
backupfp.write(encode_cfg)
|
||||
except Exception, e:
|
||||
exit(e[0], "'{}' {}".format(backup_filename, e[1]),line=inspect.getlineno(inspect.currentframe()))
|
||||
except Exception as e:
|
||||
exit(e.args[0], "'{}' {}".format(backup_filename, e[1]),line=inspect.getlineno(inspect.currentframe()))
|
||||
|
||||
# binary format
|
||||
elif backupfileformat.lower() == FileType.BIN.lower():
|
||||
|
@ -2884,8 +2937,8 @@ def Backup(backupfile, backupfileformat, encode_cfg, decode_cfg, configmapping):
|
|||
with open(backup_filename, "wb") as backupfp:
|
||||
backupfp.write(struct.pack('<L',BINARYFILE_MAGIC))
|
||||
backupfp.write(decode_cfg)
|
||||
except Exception, e:
|
||||
exit(e[0], "'{}' {}".format(backup_filename, e[1]),line=inspect.getlineno(inspect.currentframe()))
|
||||
except Exception as e:
|
||||
exit(e.args[0], "'{}' {}".format(backup_filename, e[1]),line=inspect.getlineno(inspect.currentframe()))
|
||||
|
||||
# JSON format
|
||||
elif backupfileformat.lower() == FileType.JSON.lower():
|
||||
|
@ -2896,8 +2949,8 @@ def Backup(backupfile, backupfileformat, encode_cfg, decode_cfg, configmapping):
|
|||
try:
|
||||
with open(backup_filename, "w") as backupfp:
|
||||
json.dump(configmapping, backupfp, sort_keys=args.jsonsort, indent=None if args.jsonindent < 0 else args.jsonindent, separators=(',', ':') if args.jsoncompact else (', ', ': ') )
|
||||
except Exception, e:
|
||||
exit(e[0], "'{}' {}".format(backup_filename, e[1]),line=inspect.getlineno(inspect.currentframe()))
|
||||
except Exception as e:
|
||||
exit(e.args[0], "'{}' {}".format(backup_filename, e[1]),line=inspect.getlineno(inspect.currentframe()))
|
||||
|
||||
if args.verbose:
|
||||
srctype = 'device'
|
||||
|
@ -2940,8 +2993,8 @@ def Restore(restorefile, backupfileformat, encode_cfg, decode_cfg, configmapping
|
|||
try:
|
||||
with open(restorefilename, "rb") as restorefp:
|
||||
new_encode_cfg = restorefp.read()
|
||||
except Exception, e:
|
||||
exit(e[0], "'{}' {}".format(restorefilename, e[1]),line=inspect.getlineno(inspect.currentframe()))
|
||||
except Exception as e:
|
||||
exit(e.args[0], "'{}' {}".format(restorefilename, e[1]),line=inspect.getlineno(inspect.currentframe()))
|
||||
|
||||
elif filetype == FileType.BIN:
|
||||
if args.verbose:
|
||||
|
@ -2949,8 +3002,8 @@ def Restore(restorefile, backupfileformat, encode_cfg, decode_cfg, configmapping
|
|||
try:
|
||||
with open(restorefilename, "rb") as restorefp:
|
||||
restorebin = restorefp.read()
|
||||
except Exception, e:
|
||||
exit(e[0], "'{}' {}".format(restorefilename, e[1]),line=inspect.getlineno(inspect.currentframe()))
|
||||
except Exception as e:
|
||||
exit(e.args[0], "'{}' {}".format(restorefilename, e[1]),line=inspect.getlineno(inspect.currentframe()))
|
||||
header = struct.unpack_from('<L', restorebin, 0)[0]
|
||||
if header == BINARYFILE_MAGIC:
|
||||
decode_cfg = restorebin[4:] # remove header from encrypted config file
|
||||
|
@ -3004,8 +3057,8 @@ def Restore(restorefile, backupfileformat, encode_cfg, decode_cfg, configmapping
|
|||
try:
|
||||
with open(args.tasmotafile, "wb") as outputfile:
|
||||
outputfile.write(new_encode_cfg)
|
||||
except Exception, e:
|
||||
exit(e[0], "'{}' {}".format(args.tasmotafile, e[1]),line=inspect.getlineno(inspect.currentframe()))
|
||||
except Exception as e:
|
||||
exit(e.args[0], "'{}' {}".format(args.tasmotafile, e[1]),line=inspect.getlineno(inspect.currentframe()))
|
||||
if args.verbose:
|
||||
message("Restore successful to file '{}' using restore file '{}'".format(args.tasmotafile, restorefilename), type_=LogType.INFO)
|
||||
|
||||
|
@ -3026,10 +3079,10 @@ def OutputTasmotaCmnds(tasmotacmnds):
|
|||
def OutputTasmotaSubCmnds(cmnds):
|
||||
if args.cmndsort:
|
||||
for cmnd in sorted(cmnds, key = lambda cmnd:[int(c) if c.isdigit() else c for c in re.split('(\d+)', cmnd)]):
|
||||
print "{}{}".format(" "*args.cmndindent, cmnd)
|
||||
print("{}{}".format(" "*args.cmndindent, cmnd))
|
||||
else:
|
||||
for cmnd in cmnds:
|
||||
print "{}{}".format(" "*args.cmndindent, cmnd)
|
||||
print("{}{}".format(" "*args.cmndindent, cmnd))
|
||||
|
||||
groups = GetGroupList(Settings[0][2])
|
||||
|
||||
|
@ -3038,7 +3091,7 @@ def OutputTasmotaCmnds(tasmotacmnds):
|
|||
if group.title() in (groupname.title() for groupname in tasmotacmnds):
|
||||
cmnds = tasmotacmnds[group]
|
||||
print
|
||||
print "# {}:".format(group)
|
||||
print("# {}:".format(group))
|
||||
OutputTasmotaSubCmnds(cmnds)
|
||||
|
||||
else:
|
||||
|
@ -3245,10 +3298,10 @@ def ParseArgs():
|
|||
args = parser.parse_args()
|
||||
|
||||
if args.debug >= 1:
|
||||
print >> sys.stderr, parser.format_values()
|
||||
print >> sys.stderr, "Settings:"
|
||||
print(parser.format_values(), file=sys.stderr)
|
||||
print("Settings:", file=sys.stderr)
|
||||
for k in args.__dict__:
|
||||
print >> sys.stderr, " "+str(k), "= ",eval('args.{}'.format(k))
|
||||
print(" "+str(k), "= ",eval('args.{}'.format(k)), file=sys.stderr)
|
||||
return args
|
||||
|
||||
|
||||
|
@ -3280,7 +3333,7 @@ if __name__ == "__main__":
|
|||
# no config source given
|
||||
ShortHelp(False)
|
||||
print
|
||||
print parser.epilog
|
||||
print(parser.epilog)
|
||||
sys.exit(ExitCode.OK)
|
||||
|
||||
if len(encode_cfg) == 0:
|
||||
|
@ -3309,7 +3362,7 @@ if __name__ == "__main__":
|
|||
# json screen output
|
||||
if (args.backupfile is None and args.restorefile is None) or args.output:
|
||||
if args.outputformat == 'json':
|
||||
print json.dumps(configmapping, sort_keys=args.jsonsort, indent=None if args.jsonindent<0 else args.jsonindent, separators=(',', ':') if args.jsoncompact else (', ', ': ') )
|
||||
print(json.dumps(configmapping, sort_keys=args.jsonsort, indent=None if args.jsonindent<0 else args.jsonindent, separators=(',', ':') if args.jsoncompact else (', ', ': ') ))
|
||||
|
||||
if args.outputformat == 'cmnd' or args.outputformat == 'command':
|
||||
tasmotacmnds = Mapping2Cmnd(decode_cfg, configmapping)
|
||||
|
|
Loading…
Reference in New Issue