decode-config.py: adapt settings, rename Sonoff->Tasmota, prep for Python3

- add SetOption73 (cors_enabled)
- add SetOption74 (ds18x20_internal_pullup)
- add SetOption75 (grouptopic_mode)
- add SetOption76 (bootcount_update)
- add ShutterMotorDelay (shutter_motordelay)
- add SetOption82-113 (flag4)
- add i2c_drivers
- add WifiPower (wifi_output_power)
- add TempOffset (temp_comp)
- remove SetOption2 (value_units)
- rename SetOption66 (tuya_dimmer_range_255 > tuya_serial_mqtt_publish)
- rename Sonoff > Tasmota
- preparation for Python3 match
This commit is contained in:
Norbert Richter 2019-11-20 12:50:38 +01:00
parent 40b3d20e0d
commit 7276c2a658
3 changed files with 184 additions and 112 deletions

View File

@ -6,7 +6,7 @@
<li>can restore previously backup and changed <a href="http://www.json.org/">JSON</a>-format files,</li> <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> <li>is able to create Tasmota compatible command list with related config parameter</li>
</ul> </ul>
<p>Comparing backup files created by <em>decode-config.py</em> and *.dmp files created by Tasmota &quot;Backup/Restore Configuration&quot;: </p> <p>Comparing backup files created by <em>decode-config.py</em> and *.dmp files created by Tasmota &quot;Backup/Restore Configuration&quot;:</p>
<table> <table>
<thead> <thead>
<tr> <tr>
@ -75,23 +75,39 @@
</ul> </ul>
<h2 id="prerequisite">Prerequisite</h2> <h2 id="prerequisite">Prerequisite</h2>
<ul> <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>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>
</li> </ul>
<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> <h3 id="linux">Linux</h3>
<ul> <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 pycurl7.43.0.3cp27cp27mwin_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>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> <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> </ul>
</li> </li>
</ul> </ul>
<h2 id="file-types">File Types</h2> <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> <h3 id="-dmp-format">.dmp Format</h3>
<p>Configuration data as used by Tasmota &quot;Backup/Restore Configuration&quot; web interface.<br>This format is binary and encrypted.</p> <p>Configuration data as used by Tasmota &quot;Backup/Restore Configuration&quot; web interface.
This format is binary and encrypted.</p>
<h3 id="-json-format">.json Format</h3> <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> <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 &quot;Backup/Restore Configuration&quot; 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 &quot;Backup/Restore Configuration&quot; 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> <h4 id="file-extensions">File extensions</h4>
<p>You don&#39;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. <p>You don&#39;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> 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&#39;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> <p>After download don&#39;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> <h3 id="basics">Basics</h3>
<p>At least pass a source where you want to read the configuration data from using <code>-f &lt;filename&gt;</code> or <code>-d &lt;host&gt;</code>:</p> <p>At least pass a source where you want to read the configuration data from using <code>-f &lt;filename&gt;</code> or <code>-d &lt;host&gt;</code>:</p>
<p>The source can be either </p> <p>The source can be either</p>
<ul> <ul>
<li>a Tasmota device hostname or IP using the <code>-d &lt;host&gt;</code> parameter</li> <li>a Tasmota device hostname or IP using the <code>-d &lt;host&gt;</code> parameter</li>
<li>a Tasmota <code>*.dmp</code> configuration file using <code>-f &lt;filename&gt;</code> parameter</li> <li>a Tasmota <code>*.dmp</code> configuration file using <code>-f &lt;filename&gt;</code> parameter</li>
</ul> </ul>
<p>Example: </p> <p>Example:</p>
<pre><code>decode-config<span class="hljs-selector-class">.py</span> -d sonoff-<span class="hljs-number">4281</span> <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> </code></pre><p>will output a human readable configuration in <a href="http://www.json.org/">JSON</a>-format:</p>
<pre><code>{ <pre><code>{
<span class="hljs-string">"altitude"</span>: <span class="hljs-number">112</span>, <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> </code></pre><h3 id="save-backup-file">Save backup file</h3>
<p>To save the output as backup file use <code>--backup-file &lt;filename&gt;</code>, you can use placeholder for Version, Friendlyname and Hostname: </p> <p>To save the output as backup file use <code>--backup-file &lt;filename&gt;</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> <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> </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> &lt;yourpassword&gt; --backup-file Config_@f_@v <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> &lt;yourpassword&gt; --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> </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> <h3 id="restore-backup-file">Restore backup file</h3>
<p>Reading back a saved (and possible changed) backup file use the <code>--restore-file &lt;filename&gt;</code> parameter. This will read the (changed) configuration data from this file and send it back to the source device or filename.</p> <p>Reading back a saved (and possible changed) backup file use the <code>--restore-file &lt;filename&gt;</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> <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">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> <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> </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> &lt;<span class="hljs-selector-tag">yourpassword</span>&gt; <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> &lt;<span class="hljs-selector-tag">yourpassword</span>&gt; <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> </code></pre><h3 id="output-to-screen">Output to screen</h3>
<p>To force screen output use the <code>--output</code> parameter.</p> <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> <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> <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>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> <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> <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><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> <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">Wifi</span>:
<span class="hljs-selector-tag">AP</span> 0 <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 <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> </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> <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>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>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>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> <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> </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>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> <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">username</span> = admin
<span class="hljs-attr">password</span> = myPaszxwo!z <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> </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> </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>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> <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 &lt;indent&gt;] [--cmnd-groups] [--cmnd-indent &lt;indent&gt;] [--cmnd-groups]
[--cmnd-nogroups] [--cmnd-<span class="hljs-keyword">sort</span>] [--cmnd-unsort] [--cmnd-nogroups] [--cmnd-<span class="hljs-keyword">sort</span>] [--cmnd-unsort]
[-c &lt;filename&gt;] [-S] [-T json|cmnd|command] [-c &lt;filename&gt;] [-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] [--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 '--' 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) (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 -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') <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> limit data processing to command groups (default <span class="hljs-keyword">no</span>
filter) 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 --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> </code></pre><h4 id="using-tasmota-binary-configuration-files">Using Tasmota binary configuration files</h4>
<ol> <ol>
<li><p>Restore a Tasmota configuration file</p> <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>
<li><p>Backup device using Tasmota configuration compatible format</p> <li><p>Backup device using Tasmota configuration compatible format</p>
<p>a) use file extension to choice the file 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>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> </li>
</ol> </ol>
<h4 id="use-batch-processing">Use batch processing</h4> <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> </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 <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 sonoff1, sonoff2 and sonoff3 using friendly name and Tasmota firmware version for backup filenames.</p> </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> <h2 id="notes">Notes</h2>
<p>Some general notes:</p> <p>Some general notes:</p>
<ul> <ul>
<li>Filename replacement macros <strong>@h</strong> and <strong>@H</strong>:<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 &lt;host&gt;</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 &lt;filename&gt;</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 &#39;%&#39; characters.</li> <li><strong>@h</strong>
<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> The <strong>@h</strong> replacement macro uses the hostname configured with the Tasomta Wifi <code>Hostname &lt;host&gt;</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 &lt;filename&gt;</code> as source).
To prevent having a useless % in your filename, <strong>@h</strong> will not replaced by configuration data hostname if this contains &#39;%&#39; 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> </ul>
</li> </li>
</ul> </ul>

View File

@ -211,7 +211,7 @@ Note: A few very specific module commands like MPC230xx, KNX and some Display co
### Filter data ### 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. 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). 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-indent <indent>] [--cmnd-groups]
[--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort] [--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort]
[-c <filename>] [-S] [-T json|cmnd|command] [-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] [--ignore-warnings] [-h] [-H] [-v] [-V]
Backup/Restore Tasmota configuration data. Args that start with '--' 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) (default do not output on backup or restore usage)
-T, --output-format json|cmnd|command -T, --output-format json|cmnd|command
display output format (default: 'json') 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 limit data processing to command groups (default no
filter) filter)
--ignore-warnings do not exit on warnings. Not recommended, used by your --ignore-warnings do not exit on warnings. Not recommended, used by your

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
VER = '2.3.0036' from __future__ import print_function
VER = '2.4.0037'
""" """
decode-config.py - Backup/Restore Tasmota configuration data 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-indent <indent>] [--cmnd-groups]
[--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort] [--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort]
[-c <filename>] [-S] [-T json|cmnd|command] [-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] [--ignore-warnings] [-h] [-H] [-v] [-V]
Backup/Restore Tasmota configuration data. Args that start with '--' 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) (default do not output on backup or restore usage)
-T, --output-format json|cmnd|command -T, --output-format json|cmnd|command
display output format (default: 'json') 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 limit data processing to command groups (default no
filter) filter)
--ignore-warnings do not exit on warnings. Not recommended, used by your --ignore-warnings do not exit on warnings. Not recommended, used by your
@ -184,7 +185,7 @@ import io
import sys, platform import sys, platform
def ModuleImportError(module): def ModuleImportError(module):
er = str(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) sys.exit(ExitCode.MODULE_NOT_FOUND)
try: try:
from datetime import datetime from datetime import datetime
@ -198,8 +199,11 @@ try:
import json import json
import configargparse import configargparse
import pycurl import pycurl
import urllib2 if sys.version_info.major==2:
except ImportError, e: import urllib2
else:
import urllib
except ImportError as e:
ModuleImportError(e) ModuleImportError(e)
# ====================================================================== # ======================================================================
@ -276,7 +280,7 @@ Settings dictionary describes the config file fields definition:
defines the use of data at <addrdef> defines the use of data at <addrdef>
format is defined in 'struct module format string' format is defined in 'struct module format string'
see 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> <setting>: <setting>
A dictionary describes a (sub)setting dictonary A dictionary describes a (sub)setting dictonary
and can recursively define another <setting> 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($)')) ), 'pulse_counter_type4': ('<H', (0x5D0,1,3), (None, None, ('Sensor', '"CounterType4 {}".format($)')) ),
}, 0x5D0, (None, None, ('Sensor', None)), (None, None) ), }, 0x5D0, (None, None, ('Sensor', None)), (None, None) ),
'pulse_counter_debounce': ('<H', 0x5D2, (None, '0 <= $ <= 3200', ('Sensor', '"CounterDebounce {}".format($)')) ), '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) 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($)')) ), 'mqttlog_level': ('B', 0x1E7, (None, None, ('Management', '"MqttLog {}".format($)')) ),
'pcf8574_config': ('B', 0xE88, ([8], None, ('Devices', None)) ), 'pcf8574_config': ('B', 0xE88, ([8], None, ('Devices', None)) ),
'shutter_accuracy': ('B', 0x1E6, (None, None, ('Shutter', None)) ), 'shutter_accuracy': ('B', 0x1E6, (None, None, ('Shutter', None)) ),
'shutter_opentime': ('<H', 0xE40, ([4], None, ('Shutter', '"ShutterOpenDuration{} {:.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)')) ), 'shutter_closetime': ('<H', 0xE48, ([4], None, ('Shutter', '"ShutterCloseDuration{} {:.1f}".format(#,$/10.0)')) ),
'shuttercoeff': ('<H', 0xE50, ([5,4],None, ('Shutter', None)) ), 'shuttercoeff': ('<H', 0xE50, ([5,4],None, ('Shutter', None)) ),
'shutter_invert': ('B', 0xE78, ([4], None, ('Shutter', '"ShutterInvert{} {}".format(#,$)')) ), 'shutter_invert': ('B', 0xE78, ([4], None, ('Shutter', '"ShutterInvert{} {}".format(#,$)')) ),
'shutter_set50percent': ('B', 0xE7C, ([4], None, ('Shutter', '"ShutterSetHalfway{} {}".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($)')) ), '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 = [ 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), (0x6060014,0x1000, Setting_6_6_0_20),
(0x6060012,0x1000, Setting_6_6_0_18), (0x6060012,0x1000, Setting_6_6_0_18),
(0x606000F,0x1000, Setting_6_6_0_15), (0x606000F,0x1000, Setting_6_6_0_15),
@ -1162,13 +1214,14 @@ def message(msg, type_=None, status=None, line=None):
@param status: @param status:
status number status number
""" """
print >> sys.stderr, '{styp}{sdelimiter}{sstatus}{slineno}{scolon}{smgs}'.format(\ print('{styp}{sdelimiter}{sstatus}{slineno}{scolon}{smgs}'.format(\
styp=type_ if type_ 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 '', 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 '', sstatus=status if status is not None and status > 0 else '',
scolon=': ' if type_ is not None or line is not None else '', scolon=': ' if type_ is not None or line is not None else '',
smgs=msg, smgs=msg,
slineno=' (@{:04d})'.format(line) if line is not None else '') 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): 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: @param doexit:
sys.exit with OK if True sys.exit with OK if True
""" """
print parser.description print(parser.description)
print print
parser.print_usage() parser.print_usage()
print 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: if doexit:
sys.exit(ExitCode.OK) sys.exit(ExitCode.OK)
@ -1554,8 +1607,8 @@ def LoadTasmotaConfig(filename):
try: try:
with open(filename, "rb") as tasmotafile: with open(filename, "rb") as tasmotafile:
encode_cfg = tasmotafile.read() encode_cfg = tasmotafile.read()
except Exception, e: except Exception as e:
exit(e[0], "'{}' {}".format(filename, e[1]),line=inspect.getlineno(inspect.currentframe())) exit(e.args[0], "'{}' {}".format(filename, e[1]),line=inspect.getlineno(inspect.currentframe()))
return encode_cfg return encode_cfg
@ -1597,8 +1650,8 @@ def TasmotaGet(cmnd, host, port, username=DEFAULTS['source']['username'], passwo
c.perform() c.perform()
responsecode = c.getinfo(c.RESPONSE_CODE) responsecode = c.getinfo(c.RESPONSE_CODE)
response = header.response() response = header.response()
except Exception, e: except Exception as e:
exit(e[0], e[1],line=inspect.getlineno(inspect.currentframe())) exit(e.args[0], e.args[1], line=inspect.getlineno(inspect.currentframe()))
finally: finally:
c.close() c.close()
@ -1727,8 +1780,8 @@ def PushTasmotaConfig(encode_cfg, host, port, username=DEFAULTS['source']['usern
try: try:
c.perform() c.perform()
responsecode = c.getinfo(c.RESPONSE_CODE) responsecode = c.getinfo(c.RESPONSE_CODE)
except Exception, e: except Exception as e:
return e[0], e[1] return e.args[0], e.args[1]
c.close() c.close()
@ -1839,7 +1892,7 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
# calling with nothing is wrong # calling with nothing is wrong
if fielddef is None: if fielddef is None:
print >> sys.stderr, '<fielddef> is None' print('<fielddef> is None', file=sys.stderr)
raise SyntaxError('<fielddef> error') raise SyntaxError('<fielddef> error')
# get top level items # get top level items
@ -1850,7 +1903,7 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
# converter present # converter present
format_, addrdef, datadef, converter = fielddef format_, addrdef, datadef, converter = fielddef
else: 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') raise SyntaxError('<fielddef> error')
# ignore calls with 'root' setting # ignore calls with 'root' setting
@ -1858,7 +1911,7 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
return eval(fields) return eval(fields)
if not isinstance(format_, (unicode,str,dict)): 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') raise SyntaxError('<fielddef> error')
# extract addrdef items # extract addrdef items
@ -1868,16 +1921,16 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
# baseaddr bit definition # baseaddr bit definition
baseaddr, bits, bitshift = baseaddr baseaddr, bits, bitshift = baseaddr
if not isinstance(bits, int): 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') raise SyntaxError('<fielddef> error')
if not isinstance(bitshift, int): 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') raise SyntaxError('<fielddef> error')
else: 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') raise SyntaxError('<fielddef> error')
if not isinstance(baseaddr, int): 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') raise SyntaxError('<fielddef> error')
# extract datadef items # 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: if isinstance(cmd, (tuple)) and len(cmd) == 2:
group, tasmotacmnd = cmd group, tasmotacmnd = cmd
if group is not None and not isinstance(group, (str, unicode)): 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') raise SyntaxError('<fielddef> error')
if tasmotacmnd is isinstance(tasmotacmnd, tuple): if tasmotacmnd is isinstance(tasmotacmnd, tuple):
tasmotacmnds = tasmotacmnd tasmotacmnds = tasmotacmnd
for tasmotacmnd in tasmotacmnds: for tasmotacmnd in tasmotacmnds:
if tasmotacmnd is not None and not callable(tasmotacmnd) and not isinstance(tasmotacmnd, (str, unicode)): 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') raise SyntaxError('<fielddef> error')
else: else:
if tasmotacmnd is not None and not callable(tasmotacmnd) and not isinstance(tasmotacmnd, (str, unicode)): 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') raise SyntaxError('<fielddef> error')
else: 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') raise SyntaxError('<fielddef> error')
else: 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') raise SyntaxError('<fielddef> error')
if validate is not None and (not isinstance(validate, (unicode,str)) and not callable(validate)): 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') raise SyntaxError('<fielddef> error')
# convert single int into one-dimensional list # convert single int into one-dimensional list
@ -1921,7 +1974,7 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
arraydef = [arraydef] arraydef = [arraydef]
if arraydef is not None and not isinstance(arraydef, (list)): 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') raise SyntaxError('<fielddef> error')
# get read/write converter items # get read/write converter items
@ -1931,13 +1984,13 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
# converter has read/write converter # converter has read/write converter
readconverter, writeconverter = converter readconverter, writeconverter = converter
if readconverter is not None and not isinstance(readconverter, (str,unicode)) and not callable(readconverter): 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') raise SyntaxError('<fielddef> error')
if writeconverter is not None and (not isinstance(writeconverter, (bool,str,unicode)) and not callable(writeconverter)): 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') raise SyntaxError('<fielddef> error')
else: 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') raise SyntaxError('<fielddef> error')
@ -1975,8 +2028,8 @@ def ReadWriteConverter(value, fielddef, read=True, raw=False):
return eval(conv.replace('$','value')) return eval(conv.replace('$','value'))
elif callable(conv): # use as format function elif callable(conv): # use as format function
return conv(value) return conv(value)
except Exception, e: except Exception as e:
exit(e[0], e[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) exit(e.args[0], e.args[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe()))
return value return value
@ -2294,7 +2347,7 @@ def SetFieldValue(fielddef, dobj, addr, value):
formatcnt = GetFormatCount(format_) formatcnt = GetFormatCount(format_)
singletype, bitsize = GetFormatType(format_) singletype, bitsize = GetFormatType(format_)
if args.debug >= 2: 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']: if not format_[-1:].lower() in ['s','p']:
addr += (bitsize / 8) * formatcnt addr += (bitsize / 8) * formatcnt
for _ in range(0, 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: if isinstance(value,int) and value < 0 and val > maxsigned:
val = ((maxunsigned+1)-val) * (-1) val = ((maxunsigned+1)-val) * (-1)
if args.debug >= 3: 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: try:
struct.pack_into(singletype, dobj, addr, val) struct.pack_into(singletype, dobj, addr, val)
except struct.error as e: except struct.error as e:
@ -2317,7 +2370,7 @@ def SetFieldValue(fielddef, dobj, addr, value):
value >>= bitsize value >>= bitsize
else: else:
if args.debug >= 3: 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: try:
struct.pack_into(format_, dobj, addr, value) struct.pack_into(format_, dobj, addr, value)
except struct.error as e: except struct.error as e:
@ -2427,7 +2480,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""):
# do not write readonly values # do not write readonly values
if writeconverter is False: if writeconverter is False:
if args.debug >= 2: 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 return dobj
# <arraydef> contains a list # <arraydef> contains a list
@ -2465,16 +2518,16 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""):
if format_[-1:] in ['c']: if format_[-1:] in ['c']:
try: try:
value = ReadWriteConverter(restore.encode(STR_ENCODING)[0], fielddef, read=False) value = ReadWriteConverter(restore.encode(STR_ENCODING)[0], fielddef, read=False)
except Exception, e: except Exception as e:
exit(e[0], e[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) exit(e.args[0], e.args[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe()))
valid = False valid = False
# bool # bool
elif format_[-1:] in ['?']: elif format_[-1:] in ['?']:
try: try:
value = ReadWriteConverter(bool(restore), fielddef, read=False) value = ReadWriteConverter(bool(restore), fielddef, read=False)
except Exception, e: except Exception as e:
exit(e[0], e[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe())) exit(e.args[0], e.args[1], type_=LogType.WARNING, line=inspect.getlineno(inspect.currentframe()))
valid = False valid = False
# integer # integer
@ -2560,7 +2613,7 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""):
if args.debug >= 2: if args.debug >= 2:
sbits = " {} bits shift {}".format(bits, bitshift) if bits else "" sbits = " {} bits shift {}".format(bits, bitshift) if bits else ""
strvalue = "{} [{}]".format(_value, hex(value)) if isinstance(_value, int) else _value 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 != '_': if fieldname != 'cfg_crc' and fieldname != '_':
prevvalue = GetFieldValue(fielddef, dobj, baseaddr+addroffset) prevvalue = GetFieldValue(fielddef, dobj, baseaddr+addroffset)
dobj = SetFieldValue(fielddef, dobj, baseaddr+addroffset, value) 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) message("Value for '{}' changed from {} to {}".format(fieldname, prevvalue, curvalue), type_=LogType.INFO)
else: else:
if args.debug >= 2: 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: else:
sformat = "file '{sfile}' - {{'{sname}': {svalue}}} ({serror})"+errformat 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) 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: try:
with open(backup_filename, "wb") as backupfp: with open(backup_filename, "wb") as backupfp:
backupfp.write(encode_cfg) backupfp.write(encode_cfg)
except Exception, e: except Exception as e:
exit(e[0], "'{}' {}".format(backup_filename, e[1]),line=inspect.getlineno(inspect.currentframe())) exit(e.args[0], "'{}' {}".format(backup_filename, e[1]),line=inspect.getlineno(inspect.currentframe()))
# binary format # binary format
elif backupfileformat.lower() == FileType.BIN.lower(): 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: with open(backup_filename, "wb") as backupfp:
backupfp.write(struct.pack('<L',BINARYFILE_MAGIC)) backupfp.write(struct.pack('<L',BINARYFILE_MAGIC))
backupfp.write(decode_cfg) backupfp.write(decode_cfg)
except Exception, e: except Exception as e:
exit(e[0], "'{}' {}".format(backup_filename, e[1]),line=inspect.getlineno(inspect.currentframe())) exit(e.args[0], "'{}' {}".format(backup_filename, e[1]),line=inspect.getlineno(inspect.currentframe()))
# JSON format # JSON format
elif backupfileformat.lower() == FileType.JSON.lower(): elif backupfileformat.lower() == FileType.JSON.lower():
@ -2896,8 +2949,8 @@ def Backup(backupfile, backupfileformat, encode_cfg, decode_cfg, configmapping):
try: try:
with open(backup_filename, "w") as backupfp: 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 (', ', ': ') ) 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: except Exception as e:
exit(e[0], "'{}' {}".format(backup_filename, e[1]),line=inspect.getlineno(inspect.currentframe())) exit(e.args[0], "'{}' {}".format(backup_filename, e[1]),line=inspect.getlineno(inspect.currentframe()))
if args.verbose: if args.verbose:
srctype = 'device' srctype = 'device'
@ -2940,8 +2993,8 @@ def Restore(restorefile, backupfileformat, encode_cfg, decode_cfg, configmapping
try: try:
with open(restorefilename, "rb") as restorefp: with open(restorefilename, "rb") as restorefp:
new_encode_cfg = restorefp.read() new_encode_cfg = restorefp.read()
except Exception, e: except Exception as e:
exit(e[0], "'{}' {}".format(restorefilename, e[1]),line=inspect.getlineno(inspect.currentframe())) exit(e.args[0], "'{}' {}".format(restorefilename, e[1]),line=inspect.getlineno(inspect.currentframe()))
elif filetype == FileType.BIN: elif filetype == FileType.BIN:
if args.verbose: if args.verbose:
@ -2949,8 +3002,8 @@ def Restore(restorefile, backupfileformat, encode_cfg, decode_cfg, configmapping
try: try:
with open(restorefilename, "rb") as restorefp: with open(restorefilename, "rb") as restorefp:
restorebin = restorefp.read() restorebin = restorefp.read()
except Exception, e: except Exception as e:
exit(e[0], "'{}' {}".format(restorefilename, e[1]),line=inspect.getlineno(inspect.currentframe())) exit(e.args[0], "'{}' {}".format(restorefilename, e[1]),line=inspect.getlineno(inspect.currentframe()))
header = struct.unpack_from('<L', restorebin, 0)[0] header = struct.unpack_from('<L', restorebin, 0)[0]
if header == BINARYFILE_MAGIC: if header == BINARYFILE_MAGIC:
decode_cfg = restorebin[4:] # remove header from encrypted config file decode_cfg = restorebin[4:] # remove header from encrypted config file
@ -3004,8 +3057,8 @@ def Restore(restorefile, backupfileformat, encode_cfg, decode_cfg, configmapping
try: try:
with open(args.tasmotafile, "wb") as outputfile: with open(args.tasmotafile, "wb") as outputfile:
outputfile.write(new_encode_cfg) outputfile.write(new_encode_cfg)
except Exception, e: except Exception as e:
exit(e[0], "'{}' {}".format(args.tasmotafile, e[1]),line=inspect.getlineno(inspect.currentframe())) exit(e.args[0], "'{}' {}".format(args.tasmotafile, e[1]),line=inspect.getlineno(inspect.currentframe()))
if args.verbose: if args.verbose:
message("Restore successful to file '{}' using restore file '{}'".format(args.tasmotafile, restorefilename), type_=LogType.INFO) 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): def OutputTasmotaSubCmnds(cmnds):
if args.cmndsort: 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)]): 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: else:
for cmnd in cmnds: for cmnd in cmnds:
print "{}{}".format(" "*args.cmndindent, cmnd) print("{}{}".format(" "*args.cmndindent, cmnd))
groups = GetGroupList(Settings[0][2]) groups = GetGroupList(Settings[0][2])
@ -3038,7 +3091,7 @@ def OutputTasmotaCmnds(tasmotacmnds):
if group.title() in (groupname.title() for groupname in tasmotacmnds): if group.title() in (groupname.title() for groupname in tasmotacmnds):
cmnds = tasmotacmnds[group] cmnds = tasmotacmnds[group]
print print
print "# {}:".format(group) print("# {}:".format(group))
OutputTasmotaSubCmnds(cmnds) OutputTasmotaSubCmnds(cmnds)
else: else:
@ -3245,10 +3298,10 @@ def ParseArgs():
args = parser.parse_args() args = parser.parse_args()
if args.debug >= 1: if args.debug >= 1:
print >> sys.stderr, parser.format_values() print(parser.format_values(), file=sys.stderr)
print >> sys.stderr, "Settings:" print("Settings:", file=sys.stderr)
for k in args.__dict__: 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 return args
@ -3280,7 +3333,7 @@ if __name__ == "__main__":
# no config source given # no config source given
ShortHelp(False) ShortHelp(False)
print print
print parser.epilog print(parser.epilog)
sys.exit(ExitCode.OK) sys.exit(ExitCode.OK)
if len(encode_cfg) == 0: if len(encode_cfg) == 0:
@ -3309,7 +3362,7 @@ if __name__ == "__main__":
# json screen output # json screen output
if (args.backupfile is None and args.restorefile is None) or args.output: if (args.backupfile is None and args.restorefile is None) or args.output:
if args.outputformat == 'json': 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': if args.outputformat == 'cmnd' or args.outputformat == 'command':
tasmotacmnds = Mapping2Cmnd(decode_cfg, configmapping) tasmotacmnds = Mapping2Cmnd(decode_cfg, configmapping)