=9) {
@@ -1263,7 +1263,7 @@ void sml_shift_in(uint32_t meters,uint32_t shard) {
}
}
sb_counter++;
- if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='p') SML_Decode(meters);
+ if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p') SML_Decode(meters);
}
@@ -1523,7 +1523,7 @@ void SML_Decode(uint8_t index) {
}
} else {
double dval;
- if (meter_desc_p[mindex].type!='e' && meter_desc_p[mindex].type!='r' && meter_desc_p[mindex].type!='m' && meter_desc_p[mindex].type!='p') {
+ if (meter_desc_p[mindex].type!='e' && meter_desc_p[mindex].type!='r' && meter_desc_p[mindex].type!='m' && meter_desc_p[mindex].type!='M' && meter_desc_p[mindex].type!='p') {
// get numeric values
if (meter_desc_p[mindex].type=='o' || meter_desc_p[mindex].type=='c') {
dval=CharToDouble((char*)cp);
@@ -2061,7 +2061,7 @@ init10:
} else {
// serial input, init
#ifdef SPECIAL_SS
- if (meter_desc_p[meters].type=='m' || meter_desc_p[meters].type=='p') {
+ if (meter_desc_p[meters].type=='m' || meter_desc_p[meters].type=='M' || meter_desc_p[meters].type=='p') {
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1);
} else {
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1,1);
@@ -2072,7 +2072,12 @@ init10:
if (meter_ss[meters]->begin(meter_desc_p[meters].params)) {
meter_ss[meters]->flush();
}
- if (meter_ss[meters]->hardwareSerial()) { ClaimSerial(); }
+ if (meter_ss[meters]->hardwareSerial()) {
+ if (meter_desc_p[meters].type=='M') {
+ Serial.begin(meter_desc_p[meters].params, SERIAL_8E1);
+ }
+ ClaimSerial();
+ }
}
}
@@ -2234,7 +2239,7 @@ void SML_Send_Seq(uint32_t meter,char *seq) {
slen++;
if (slen>=sizeof(sbuff)) break;
}
- if (script_meter_desc[meter].type=='m') {
+ if (script_meter_desc[meter].type=='m' || script_meter_desc[meter].type=='M') {
*ucp++=0;
*ucp++=2;
// append crc
diff --git a/tools/decode-config.html b/tools/decode-config.html
deleted file mode 100644
index ccd05b514..000000000
--- a/tools/decode-config.html
+++ /dev/null
@@ -1,356 +0,0 @@
-decode-config.py
-decode-config.py is able to backup and restore Tasmota configuration.
-In comparison with the Tasmota build-in "Backup/Restore Configuration" function decode-config.py
-
-- uses human readable and editable JSON-format for backup/restore,
-- can restore previously backup and changed JSON-format files,
-- is able to create Tasmota compatible command list with related config parameter
-
-Comparing backup files created by decode-config.py and *.dmp files created by Tasmota "Backup/Restore Configuration":
-
-
-
- |
-decode-config.py *.json file |
-Tasmota *.dmp file |
-
-
-
-
-Encrypted |
-No |
-Yes |
-
-
-Readable |
-Yes |
-No |
-
-
-Simply editable |
-Yes |
-No |
-
-
-Simply batch processing |
-Yes |
-No |
-
-
-
-decode-config.py is compatible with Tasmota version from v5.10.0 up to now.
-Content
-
-Prerequisite
-
-File Types
-decode-config.py can handle the following backup file types:
-
-Configuration data as used by Tasmota "Backup/Restore Configuration" web interface.
This format is binary and encrypted.
-
-Configuration data in JSON-format.
This format is decrypted, human readable and editable and can also be used for the --restore-file
parameter.
This file will be created by decode-config.py using the --backup-file
with --backup-type json
parameter, this is the default.
-
-Configuration data in binary format.
This format is binary decryptet, editable (e.g. using a hex editor) and can also be used for --restore-file
command.
It will be created by decode-config.py using --backup-file
with --backup-type bin
.
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 struct SYSCFG from Tasmota settings.h in decrypted format.
-File extensions
-You don't need to append exensions for your file name as decode-config.py uses auto extension as default. The extension will be choose based on file contents and --backup-type
parameter.
-If you do not want using auto extensions use the --no-extension
parameter.
-Usage
-After download don't forget to set the executable flag under linux with chmod +x decode-config.py
or call the program using python decode-config.py...
.
-Basics
-At least pass a source where you want to read the configuration data from using -f <filename>
or -d <host>
:
-The source can be either
-
-- a Tasmota device hostname or IP using the
-d <host>
parameter
-- a Tasmota
*.dmp
configuration file using -f <filename>
parameter
-
-Example:
-decode-config.py -d sonoff-4281
-
will output a human readable configuration in JSON-format:
-{
- "altitude": 112,
- "baudrate": 115200,
- "blinkcount": 10,
- "blinktime": 10,
-...
- "ws_width": [
- 1,
- 3,
- 5
- ]
-}
-
Save backup file
-To save the output as backup file use --backup-file <filename>
, you can use placeholder for Version, Friendlyname and Hostname:
-decode-config.py -d sonoff-4281 --backup-file Config_@f_@v
-
If you have setup a WebPassword within Tasmota, use
-decode-config.py -d sonoff-4281 -p <yourpassword> --backup-file Config_@f_@v
-
will create a file like Config_Sonoff_6.4.0.json
(the part Sonoff
and 6.4.0
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.
-Restore backup file
-Reading back a saved (and possible changed) backup file use the --restore-file <filename>
parameter. This will read the (changed) configuration data from this file and send it back to the source device or filename.
-To restore the previously save backup file Config_Sonoff_6.2.1.json
to device sonoff-4281
use:
-decode-config.py -d sonoff-4281 --restore-file Config_Sonoff_6.2.1.json
-
with password set by WebPassword:
-decode-config.py -d sonoff-4281 -p <yourpassword> --restore-file Config_Sonoff_6.2.1.json
-
Output to screen
-To force screen output use the --output
parameter.
-Output to screen is default enabled when calling the program with a source parameter (-f or -d) but without any backup or restore parameter.
-JSON output
-The default output format is JSON. You can force JSON output using the --output-format json
parameter.
-Example:
-decode-config.py -d sonoff-4281 -c my.conf -x Wifi --output-format json
-
-{
- ...
- "hostname": "%s-%04d",
- "ip_address": [
- "0.0.0.0",
- "192.168.12.1",
- "255.255.255.0",
- "192.168.12.1"
- ],
- "ntp_server": [
- "ntp.localnet.home",
- "ntp2.localnet.home",
- "192.168.12.1"
- ],
- "sta_active": 0,
- "sta_config": 5,
- "sta_pwd": [
- "myWlAnPaszxwo!z",
- "myWlAnPaszxwo!z2"
- ],
- "sta_ssid": [
- "wlan.1",
- "my-wlan"
- ],
- "web_password": "myPaszxwo!z",
- "webserver": 2
- ...
-}
-
Note: JSON output always contains all configuration data like the backup file except you are using --group
arg.
-Tasmota command output
-decode-config.py is able to translate the configuration data to (most all) Tasmota commands. To output your configuration as Tasmota commands use --output-format cmnd
or --output-format command
.
-Example:
-decode-config.py -d sonoff-4281 -c my.conf -g Wifi --output-format cmnd
-
-# Wifi:
- AP 0
- Hostname %s-%04d
- IPAddress1 0.0.0.0
- IPAddress2 192.168.12.1
- IPAddress3 255.255.255.0
- IPAddress4 192.168.12.1
- NtpServer1 ntp.localnet.home
- NtpServer2 ntp2.localnet.home
- NtpServer3 192.168.12.1
- Password1 myWlAnPaszxwo!z
- Password2 myWlAnPaszxwo!z2
- SSId1 wlan.1
- SSId2 wlan.1
- WebPassword myPaszxwo!z
- WebServer 2
- WifiConfig 5
-
Note: A few very specific module commands like MPC230xx, KNX and some Display commands are not supported. These are still available by JSON output.
-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
-These are similary to the categories on https://github.com/arendst/Tasmota/wiki/Commands.
-To filter outputs to a subset of groups use the -g
or --group
arg concatenating the grooup you want, e. g.
-decode-config.py -d sonoff-4281 -c my.conf --output-format cmnd --group Main MQTT Management Wifi
-
Configuration file
-Each argument that start with --
(eg. --file
) can also be set in a config file (specified via -c). Config file syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at https://pypi.org/project/ConfigArgParse).
-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.
-The http authentication credentials --username
and --password
is predestinated to store it in a file instead using it on your command line as argument:
-e.g. my.conf:
-[source]
-username = admin
-password = myPaszxwo!z
-
To make a backup file from example above you can now pass the config file instead using the password on command line:
-decode-config.py -d sonoff-4281 -c my.conf --backup-file Config_@f_@v
-
More program arguments
-For better reading each short written arg (minus sign -
) has a corresponding long version (two minus signs --
), eg. --device
for -d
or --file
for -f
(note: not even all --
arg has a corresponding -
one).
-A short list of possible program args is displayed using -h
or --help
.
-For advanced help use -H
or --full-help
:
-usage: decode-config.py [-f <filename>] [-d <host>] [-P <port>]
- [-u <username>] [-p <password>] [-i <filename>]
- [-o <filename>] [-t json|bin|dmp] [-E] [-e] [-F]
- [--json-indent <indent>] [--json-compact]
- [--json-hide-pw] [--json-show-pw]
- [--cmnd-indent <indent>] [--cmnd-groups]
- [--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort]
- [-c <filename>] [-S] [-T json|cmnd|command]
- [-g {Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} [{Control,Devices,Display,Domoticz,Internal,KNX,Light,MQTT,Management,Power,Rules,Sensor,Serial,SetOption,SonoffRF,System,Timer,Wifi} ...]]
- [--ignore-warnings] [-h] [-H] [-v] [-V]
-
-Backup/Restore Tasmota configuration data. Args that start with '--'
-(eg. -f) can also be set in a config file (specified via -c). Config file
-syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at
-https:
-commandline values override config file values which override defaults.
-
-Source:
- Read/Write Tasmota configuration from/to
-
- -f, --file, --tasmota-file <filename>
- file to retrieve/write Tasmota configuration from/to
- (default: None)'
- -d, --device, --host <host>
- hostname or IP address to retrieve/send Tasmota
- configuration from/to (default: None)
- -P, --port <port> TCP/IP port number to use for the host connection
- (default: 80)
- -u, --username <username>
- host HTTP access username (default: admin)
- -p, --password <password>
- host HTTP access password (default: None)
-
-Backup/Restore:
- Backup & restore specification
-
- -i, --restore-file <filename>
- file to restore configuration from (default: None).
- Replacements: @v=firmware version from config,
- @f=device friendly name from config, @h=device
- hostname from config, @H=device hostname from device
- (-d arg only)
- -o, --backup-file <filename>
- file to backup configuration to (default: None).
- Replacements: @v=firmware version from config,
- @f=device friendly name from config, @h=device
- hostname from config, @H=device hostname from device
- (-d arg only)
- -t, --backup-type json|bin|dmp
- backup filetype (default: 'json')
- -E, --extension append filetype extension for -i and -o filename
- (default)
- -e, --no-extension do not append filetype extension, use -i and -o
- filename as passed
- -F, --force-restore force restore even configuration is identical
-
-JSON output:
- JSON format specification
-
- --json-indent <indent>
- pretty-printed JSON output using indent level
- (default: 'None'). -1 disables indent.
- --json-compact compact JSON output by eliminate whitespace
- --json-hide-pw hide passwords
- --json-show-pw, --json-unhide-pw
- unhide passwords (default)
-
-Tasmota command output:
- Tasmota command output format specification
-
- --cmnd-indent <indent>
- Tasmota command grouping indent level (default: '2').
- 0 disables indent
- --cmnd-groups group Tasmota commands (default)
- --cmnd-nogroups leave Tasmota commands ungrouped
- --cmnd-sort sort Tasmota commands (default)
- --cmnd-unsort leave Tasmota commands unsorted
-
-Common:
- Optional arguments
-
- -c, --config <filename>
- program config file - can be used to set default
- command args (default: None)
- -S, --output display output regardsless of backup/restore usage
- (default do not output on backup or restore usage)
- -T, --output-format json|cmnd|command
- display output format (default: 'json')
- -g, --group {Control,Devices,Display,Domoticz,Internal,Knx,Light,Management,Mqtt,Power,Rules,Sensor,Serial,Setoption,Shutter,Sonoffrf,System,Timer,Wifi}
- limit data processing to command groups (default no
- filter)
- --ignore-warnings do not exit on warnings. Not recommended, used by your
- own responsibility!
-
-Info:
- Extra information
-
- -h, --help show usage help message and exit
- -H, --full-help show full help message and exit
- -v, --verbose produce more output about what the program does
- -V, --version show program's version number and exit
-
-Either argument -d <host> or -f <filename> must be given.
-
Program parameter notes
-decode-config.py
-Examples
-The most of the examples are for linux command line. Under Windows call the program using python decode-config.py ...
.
-Config file
-Note: The example contains .ini style sections [...]
. Sections are always treated as comment and serves as clarity only.
-For further details of config file syntax see https://pypi.org/project/ConfigArgParse.
-my.conf
-[Source]
-username = admin
-password = myPaszxwo!z
-
-[JSON]
-json-indent 2
-
Using Tasmota binary configuration files
-
-Restore a Tasmota configuration file
- decode-config.py -c my.conf -d sonoff --restore-file Config_Sonoff_6.2.1.dmp
-
-Backup device using Tasmota configuration compatible format
-a) use file extension to choice the file format
- decode-config.py -c my.conf -d sonoff --backup-file Config_@f_@v.dmp
-b) use args to choice the file format
- decode-config.py -c my.conf -d sonoff --backup-type dmp --backup-file Config_@f_@v
-
-
-Use batch processing
-for device in sonoff1 sonoff2 sonoff3; do ./decode-config.py -c my.conf -d $device -o Config_@f_@v
-
or under windows
-for device in (sonoff1 sonoff2 sonoff3) do python decode-config.py -c my.conf -d %device -o Config_@f_@v
-
will produce JSON configuration files for host sonoff1, sonoff2 and sonoff3 using friendly name and Tasmota firmware version for backup filenames.
-Notes
-Some general notes:
-
-- Filename replacement macros @h and @H:
-- @h
The @h replacement macro uses the hostname configured with the Tasomta Wifi Hostname <host>
command (defaults to %s-%04d
). It will not use the network hostname of your device because this is not available when working with files only (e.g. --file <filename>
as source).
To prevent having a useless % in your filename, @h will not replaced by configuration data hostname if this contains '%' characters.
-- @H
If you want to use the network hostname within your filename, use the @H replacement macro instead - but be aware this will only replaced if you are using a network device as source (-d
, --device
, --host
); it will not work when using a file as source (-f
, --file
)
-
-
-
diff --git a/tools/decode-config.md b/tools/decode-config.md
index 43fe807f3..4097638fa 100644
--- a/tools/decode-config.md
+++ b/tools/decode-config.md
@@ -55,14 +55,14 @@ pip install pycurl configargparse
Install [Python 2.7](https://www.python.org/download/releases/2.7/) then install dependencies. For PyCurl you need to [download pycurl‑7.43.0.3‑cp27‑cp27m‑win_amd64.whl](https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycurl) for Windows 10 64bit.
```
-pip install pycurl-7.43.0.3-cp27-cp27m-win_amd64.whl configargparse
+pip install pycurl-7.43.0.3-cp27-cp27m-win_amd64.whl
// run the command from the folder where you downloaded the file
pip install configargparse
```
* [Tasmota](https://github.com/arendst/Tasmota) [Firmware](https://github.com/arendst/Tasmota/releases) with Web-Server enabled:
- * To backup or restore configurations from or to a Tasmota device you need a firmare with enabled web-server in admin mode (command [WebServer 2](https://github.com/arendst/Tasmota/wiki/Commands#wifi)). This is the Tasmota default.
+ * To backup or restore configurations from or to a Tasmota device you need a firmare with enabled web-server in admin mode (command [WebServer 2](https://tasmota.github.io/docs/#/Commands#wifi)). This is the Tasmota default.
* If using your own compiled firmware be aware to enable the web-server (`#define USE_WEBSERVER` and `#define WEB_SERVER 2`).
## File Types
@@ -211,9 +211,9 @@ 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).
+These are similary to the categories on [https://tasmota.github.io/docs/#/Commands](Tasmota Command Wiki).
To filter outputs to a subset of groups use the `-g` or `--group` arg concatenating the grooup you want, e. g.
@@ -254,7 +254,7 @@ For advanced help use `-H` or `--full-help`:
[--cmnd-indent ] [--cmnd-groups]
[--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort]
[-c ] [-S] [-T json|cmnd|command]
- [-g {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
diff --git a/tools/decode-config.py b/tools/decode-config.py
index 450c1d1cb..f15c696ae 100755
--- a/tools/decode-config.py
+++ b/tools/decode-config.py
@@ -1,6 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-VER = '2.3.0036'
+from __future__ import print_function
+from past.builtins import long
+VER = '2.4.0039'
"""
decode-config.py - Backup/Restore Tasmota configuration data
@@ -22,8 +24,8 @@ VER = '2.3.0036'
Requirements:
- - Python
- - pip install json pycurl urllib2 configargparse
+ - Python 2.x:
+ pip install json requests urllib2 configargparse
Instructions:
@@ -43,7 +45,7 @@ Usage: decode-config.py [-f ] [-d ] [-P ]
[--cmnd-indent ] [--cmnd-groups]
[--cmnd-nogroups] [--cmnd-sort] [--cmnd-unsort]
[-c ] [-S] [-T json|cmnd|command]
- [-g {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 +125,7 @@ Usage: decode-config.py [-f ] [-d ] [-P ]
(default do not output on backup or restore usage)
-T, --output-format json|cmnd|command
display output format (default: 'json')
- -g, --group {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 +186,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
@@ -197,9 +199,12 @@ try:
import inspect
import json
import configargparse
- import pycurl
- import urllib2
-except ImportError, e:
+ import requests
+ if sys.version_info.major==2:
+ import urllib2
+ else:
+ import urllib
+except ImportError as e:
ModuleImportError(e)
# ======================================================================
@@ -276,7 +281,7 @@ Settings dictionary describes the config file fields definition:
defines the use of data at
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
:
A dictionary describes a (sub)setting dictonary
and can recursively define another
@@ -556,7 +561,7 @@ Setting_5_10_0 = {
'pulse_counter_type4': ('> 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):
@@ -1193,6 +1247,38 @@ def exit(status=0, msg="end", type_=LogType.ERROR, src=None, doexit=True, line=N
sys.exit(exitcode)
+def debug(args):
+ """
+ Get debug level
+
+ @param args:
+ configargparse.parse_args() result
+
+ @return:
+ debug level
+ """
+ return 0 if args.debug is None else args.debug
+
+
+def instance(type_):
+ """
+ Creates Python2/3 compatible isinstance test type(s)
+
+ @param args:
+ Python3 instance type
+
+ @return:
+ Python2/3 compatible isinstance type(s)
+ """
+ newtype = type_
+ if sys.version_info.major==2:
+ if type_==str:
+ newtype = (str,unicode)
+ elif isinstance(type_, tuple) and str in type_:
+ newtype = newtype + (unicode,)
+ return newtype
+
+
def ShortHelp(doexit=True):
"""
Show short help (usage) only - ued by own -h handling
@@ -1200,45 +1286,15 @@ 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)
-class HTTPHeader:
- """
- pycurl helper class retrieving the request header
- """
- def __init__(self):
- self.contents = ''
-
- def clear(self):
- self.contents = ''
-
- def store(self, _buffer):
- self.contents = "{}{}".format(self.contents, _buffer)
-
- def response(self):
- header = str(self.contents).split('\n')
- if len(header) > 0:
- return header[0].rstrip()
- return ''
-
- def contenttype(self):
- for item in str(self.contents).split('\n'):
- ditem = item.split(":")
- if ditem[0].strip().lower() == 'content-type' and len(ditem) > 1:
- return ditem[1].strip()
- return ''
-
- def __str__(self):
- return self.contents
-
-
class CustomHelpFormatter(configargparse.HelpFormatter):
"""
Class for customizing the help output
@@ -1284,9 +1340,6 @@ def GetTemplateSizes():
"""
Get all possible template sizes as list
- @param version:
- version number from read binary data to search for
-
@return:
template sizes as list []
"""
@@ -1419,7 +1472,7 @@ def GetVersionStr(version):
@return:
version string
"""
- if isinstance(version, (unicode,str)):
+ if isinstance(version, instance(str)):
version = int(version, 0)
major = ((version>>24) & 0xff)
minor = ((version>>16) & 0xff)
@@ -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
@@ -1576,43 +1629,21 @@ def TasmotaGet(cmnd, host, port, username=DEFAULTS['source']['username'], passwo
@return:
binary config data (encrypted) or None on error
"""
- body = None
# read config direct from device via http
- c = pycurl.Curl()
- buffer = io.BytesIO()
- c.setopt(c.WRITEDATA, buffer)
- header = HTTPHeader()
- c.setopt(c.HEADERFUNCTION, header.store)
- c.setopt(c.FOLLOWLOCATION, True)
- c.setopt(c.URL, MakeUrl(host, port, cmnd))
+ url = MakeUrl(host, port, cmnd)
+ auth = None
if username is not None and password is not None:
- c.setopt(c.HTTPAUTH, c.HTTPAUTH_BASIC)
- c.setopt(c.USERPWD, username + ':' + password)
- c.setopt(c.HTTPGET, True)
- c.setopt(c.VERBOSE, False)
+ auth = (username, password)
+ res = requests.get(url, auth=auth)
- responsecode = 200
- try:
- c.perform()
- responsecode = c.getinfo(c.RESPONSE_CODE)
- response = header.response()
- except Exception, e:
- exit(e[0], e[1],line=inspect.getlineno(inspect.currentframe()))
- finally:
- c.close()
+ if not res.ok:
+ exit(res.status_code, "Error on http GET request for {} - {}".format(url,res.reason), line=inspect.getlineno(inspect.currentframe()))
- if responsecode >= 400:
- exit(responsecode, 'HTTP result: {}'.format(header.response()),line=inspect.getlineno(inspect.currentframe()))
- elif contenttype is not None and header.contenttype()!=contenttype:
+ if contenttype is not None and res.headers['Content-Type']!=contenttype:
exit(ExitCode.DOWNLOAD_CONFIG_ERROR, "Device did not response properly, may be Tasmota webserver admin mode is disabled (WebServer 2)",line=inspect.getlineno(inspect.currentframe()))
- try:
- body = buffer.getvalue()
- except:
- pass
-
- return responsecode, body
+ return res.status_code, res.content
def GetTasmotaHostname(host, port, username=DEFAULTS['source']['username'], password=None):
@@ -1688,7 +1719,7 @@ def PushTasmotaConfig(encode_cfg, host, port, username=DEFAULTS['source']['usern
errorcode, errorstring
errorcode=0 if success, otherwise http response or exception code
"""
- if isinstance(encode_cfg, bytearray):
+ if isinstance(encode_cfg, (bytes,bytearray)):
encode_cfg = str(encode_cfg)
# get restore config page first to set internal Tasmota vars
@@ -1696,48 +1727,21 @@ def PushTasmotaConfig(encode_cfg, host, port, username=DEFAULTS['source']['usern
if body is None:
return responsecode, "ERROR"
- # post data
- c = pycurl.Curl()
- header = HTTPHeader()
- buffer_ = io.BytesIO()
- c.setopt(c.HEADERFUNCTION, header.store)
- c.setopt(c.WRITEFUNCTION, lambda x: None)
- c.setopt(c.WRITEDATA, buffer_)
- c.setopt(c.POST, 1)
- c.setopt(c.URL, MakeUrl(host, port, 'u2'))
+ # ~ # post data
+ url = MakeUrl(host, port, "u2")
+ auth = None
if username is not None and password is not None:
- c.setopt(c.HTTPAUTH, c.HTTPAUTH_BASIC)
- c.setopt(c.USERPWD, username + ':' + password)
- try:
- isfile = os.path.isfile(encode_cfg)
- except:
- isfile = False
- if isfile:
- c.setopt(c.HTTPPOST, [("file", (c.FORM_FILE, encode_cfg))])
- else:
- # use as binary data
- c.setopt(c.HTTPPOST, [
- ('fileupload', (
- c.FORM_BUFFER, '{sprog}_v{sver}.dmp'.format(sprog=os.path.basename(sys.argv[0]), sver=VER),
- c.FORM_BUFFERPTR, encode_cfg
- )),
- ])
+ auth = (username, password)
+ files = {'u2':('{sprog}_v{sver}.dmp'.format(sprog=os.path.basename(sys.argv[0]), sver=VER), encode_cfg)}
+ res = requests.post(url, auth=auth, files=files)
- responsecode = 200
- try:
- c.perform()
- responsecode = c.getinfo(c.RESPONSE_CODE)
- except Exception, e:
- return e[0], e[1]
+ if not res.ok:
+ exit(res.status_code, "Error on http POST request for {} - {}".format(url,res.reason), line=inspect.getlineno(inspect.currentframe()))
- c.close()
+ if res.headers['Content-Type']!='text/html':
+ exit(ExitCode.DOWNLOAD_CONFIG_ERROR, "Device did not response properly, may be Tasmota webserver admin mode is disabled (WebServer 2)",line=inspect.getlineno(inspect.currentframe()))
- if responsecode >= 400:
- return responsecode, header.response()
- elif header.contenttype() != 'text/html':
- return ExitCode.UPLOAD_CONFIG_ERROR, "Device did not response properly, may be Tasmota webserver admin mode is disabled (WebServer 2)"
-
- body = buffer_.getvalue()
+ body = res.content
findUpload = body.find("Upload")
if findUpload < 0:
@@ -1765,7 +1769,7 @@ def DecryptEncrypt(obj):
@return:
decrypted configuration (if obj contains encrypted data)
"""
- if isinstance(obj, bytearray):
+ if isinstance(obj, (bytes,bytearray)):
obj = str(obj)
dobj = obj[0:2]
for i in range(2, len(obj)):
@@ -1784,7 +1788,7 @@ def GetSettingsCrc(dobj):
2 byte unsigned integer crc value
"""
- if isinstance(dobj, bytearray):
+ if isinstance(dobj, (bytes,bytearray)):
dobj = str(dobj)
version, size, setting = GetTemplateSetting(dobj)
if version < 0x06060007 or version > 0x0606000A:
@@ -1809,7 +1813,7 @@ def GetSettingsCrc32(dobj):
4 byte unsigned integer crc value
"""
- if isinstance(dobj, bytearray):
+ if isinstance(dobj, (bytes,bytearray)):
dobj = str(dobj)
crc = 0
for i in range(0, len(dobj)-4):
@@ -1839,7 +1843,7 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
# calling with nothing is wrong
if fielddef is None:
- print >> sys.stderr, ' is None'
+ print(' is None', file=sys.stderr)
raise SyntaxError(' error')
# get top level items
@@ -1850,39 +1854,39 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
# converter present
format_, addrdef, datadef, converter = fielddef
else:
- print >> sys.stderr, 'wrong {} length ({}) in setting'.format(fielddef, len(fielddef))
+ print('wrong {} length ({}) in setting'.format(fielddef, len(fielddef)), file=sys.stderr)
raise SyntaxError(' error')
# ignore calls with 'root' setting
- if isinstance(format_, dict) and baseaddr is None and datadef is None:
+ if isinstance(format_, instance(dict)) and baseaddr is None and datadef is None:
return eval(fields)
- if not isinstance(format_, (unicode,str,dict)):
- print >> sys.stderr, 'wrong {} type {} in {}'.format(format_, type(format_), fielddef)
+ if not isinstance(format_, instance((str,dict))):
+ print('wrong {} type {} in {}'.format(format_, type(format_), fielddef), file=sys.stderr)
raise SyntaxError(' error')
# extract addrdef items
baseaddr = addrdef
- if isinstance(baseaddr, (list,tuple)):
+ if isinstance(baseaddr, instance((list,tuple))):
if len(baseaddr) == 3:
# baseaddr bit definition
baseaddr, bits, bitshift = baseaddr
- if not isinstance(bits, int):
- print >> sys.stderr, ' must be defined as integer in {}'.format(bits, fielddef)
+ if not isinstance(bits, instance(int)):
+ print(' must be defined as integer in {}'.format(bits, fielddef), file=sys.stderr)
raise SyntaxError(' error')
- if not isinstance(bitshift, int):
- print >> sys.stderr, ' must be defined as integer in {}'.format(bitshift, fielddef)
+ if not isinstance(bitshift, instance(int)):
+ print(' must be defined as integer in {}'.format(bitshift, fielddef), file=sys.stderr)
raise SyntaxError(' error')
else:
- print >> sys.stderr, 'wrong {} length ({}) in {}'.format(addrdef, len(addrdef), fielddef)
+ print('wrong {} length ({}) in {}'.format(addrdef, len(addrdef), fielddef), file=sys.stderr)
raise SyntaxError(' error')
- if not isinstance(baseaddr, int):
- print >> sys.stderr, ' must be defined as integer in {}'.format(baseaddr, fielddef)
+ if not isinstance(baseaddr, instance(int)):
+ print(' must be defined as integer in {}'.format(baseaddr, fielddef), file=sys.stderr)
raise SyntaxError(' error')
# extract datadef items
arraydef = datadef
- if isinstance(datadef, (tuple)):
+ if isinstance(datadef, instance((tuple))):
if len(datadef) == 2:
# datadef has a validator
arraydef, validate = datadef
@@ -1890,54 +1894,54 @@ def GetFieldDef(fielddef, fields="format_, addrdef, baseaddr, bits, bitshift, da
# datadef has a validator and cmd set
arraydef, validate, cmd = datadef
# cmd must be a tuple with 2 objects
- if isinstance(cmd, (tuple)) and len(cmd) == 2:
+ if isinstance(cmd, instance((tuple))) and len(cmd) == 2:
group, tasmotacmnd = cmd
- if group is not None and not isinstance(group, (str, unicode)):
- print >> sys.stderr, 'wrong {} in {}'.format(group, fielddef)
+ if group is not None and not isinstance(group, instance(str)):
+ print('wrong {} in {}'.format(group, fielddef), file=sys.stderr)
raise SyntaxError(' error')
- if tasmotacmnd is isinstance(tasmotacmnd, tuple):
+ if tasmotacmnd is isinstance(tasmotacmnd, instance(tuple)):
tasmotacmnds = tasmotacmnd
for tasmotacmnd in tasmotacmnds:
- if tasmotacmnd is not None and not callable(tasmotacmnd) and not isinstance(tasmotacmnd, (str, unicode)):
- print >> sys.stderr, 'wrong {} in {}'.format(tasmotacmnd, fielddef)
+ if tasmotacmnd is not None and not callable(tasmotacmnd) and not isinstance(tasmotacmnd, instance(str)):
+ print('wrong {} in {}'.format(tasmotacmnd, fielddef), file=sys.stderr)
raise SyntaxError(' error')
else:
- if tasmotacmnd is not None and not callable(tasmotacmnd) and not isinstance(tasmotacmnd, (str, unicode)):
- print >> sys.stderr, 'wrong {} in {}'.format(tasmotacmnd, fielddef)
+ if tasmotacmnd is not None and not callable(tasmotacmnd) and not isinstance(tasmotacmnd, instance(str)):
+ print('wrong {} in {}'.format(tasmotacmnd, fielddef), file=sys.stderr)
raise SyntaxError(' error')
else:
- print >> sys.stderr, 'wrong {} length ({}) in {}'.format(cmd, len(cmd), fielddef)
+ print('wrong {} length ({}) in {}'.format(cmd, len(cmd), fielddef), file=sys.stderr)
raise SyntaxError(' error')
else:
- print >> sys.stderr, 'wrong {} length ({}) in {}'.format(datadef, len(datadef), fielddef)
+ print('wrong {} length ({}) in {}'.format(datadef, len(datadef), fielddef), file=sys.stderr)
raise SyntaxError(' error')
- if validate is not None and (not isinstance(validate, (unicode,str)) and not callable(validate)):
- print >> sys.stderr, 'wrong {} type {} in {}'.format(validate, type(validate), fielddef)
+ if validate is not None and (not isinstance(validate, instance(str)) and not callable(validate)):
+ print('wrong {} type {} in {}'.format(validate, type(validate), fielddef), file=sys.stderr)
raise SyntaxError(' error')
# convert single int into one-dimensional list
- if isinstance(arraydef, int):
+ if isinstance(arraydef, instance(int)):
arraydef = [arraydef]
- if arraydef is not None and not isinstance(arraydef, (list)):
- print >> sys.stderr, 'wrong {} type {} in {}'.format(arraydef, type(arraydef), fielddef)
+ if arraydef is not None and not isinstance(arraydef, instance((list))):
+ print('wrong {} type {} in {}'.format(arraydef, type(arraydef), fielddef), file=sys.stderr)
raise SyntaxError(' error')
# get read/write converter items
readconverter = converter
- if isinstance(converter, (tuple)):
+ if isinstance(converter, instance((tuple))):
if len(converter) == 2:
# 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 {} type {} in {}'.format(readconverter, type(readconverter), fielddef)
+ if readconverter is not None and not isinstance(readconverter, instance(str)) and not callable(readconverter):
+ print('wrong {} type {} in {}'.format(readconverter, type(readconverter), fielddef), file=sys.stderr)
raise SyntaxError(' error')
- if writeconverter is not None and (not isinstance(writeconverter, (bool,str,unicode)) and not callable(writeconverter)):
- print >> sys.stderr, 'wrong {} type {} in {}'.format(writeconverter, type(writeconverter), fielddef)
+ if writeconverter is not None and (not isinstance(writeconverter, instance((bool,str))) and not callable(writeconverter)):
+ print('wrong {} type {} in {}'.format(writeconverter, type(writeconverter), fielddef), file=sys.stderr)
raise SyntaxError(' error')
else:
- print >> sys.stderr, 'wrong {} length ({}) in {}'.format(converter, len(converter), fielddef)
+ print('wrong {} length ({}) in {}'.format(converter, len(converter), fielddef), file=sys.stderr)
raise SyntaxError(' error')
@@ -1971,12 +1975,12 @@ def ReadWriteConverter(value, fielddef, read=True, raw=False):
if not raw and converter is not None:
conv = readconverter if read else writeconverter
try:
- if isinstance(conv, str): # evaluate strings
+ if isinstance(conv, instance(str)): # evaluate strings
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
@@ -2008,7 +2012,7 @@ def CmndConverter(valuemapping, value, idx, fielddef):
if tasmotacmnd is not None and (callable(tasmotacmnd) or len(tasmotacmnd) > 0):
if idx is not None:
idx += 1
- if isinstance(tasmotacmnd, str): # evaluate strings
+ if isinstance(tasmotacmnd, instance(str)): # evaluate strings
if idx is not None:
evalstr = tasmotacmnd.replace('$','value').replace('#','idx').replace('@','valuemapping')
else:
@@ -2047,7 +2051,7 @@ def ValidateValue(value, fielddef):
valid = True
try:
- if isinstance(validate, str): # evaluate strings
+ if isinstance(validate, instance(str)): # evaluate strings
valid = eval(validate.replace('$','value'))
elif callable(validate): # use as format function
valid = validate(value)
@@ -2068,7 +2072,7 @@ def GetFormatCount(format_):
prefix count or 1 if not specified
"""
- if isinstance(format_, str):
+ if isinstance(format_, instance(str)):
match = re.search("\s*(\d+)", format_)
if match:
return int(match.group(0))
@@ -2089,7 +2093,7 @@ def GetFormatType(format_):
formattype = format_
bitsize = 0
- if isinstance(format_, str):
+ if isinstance(format_, instance(str)):
match = re.search("\s*(\D+)", format_)
if match:
formattype = match.group(0)
@@ -2151,7 +2155,7 @@ def GetFieldLength(fielddef):
format_, addrdef, arraydef = GetFieldDef(fielddef, fields='format_, addrdef, arraydef')
# contains a integer list
- if isinstance(arraydef, list) and len(arraydef) > 0:
+ if isinstance(arraydef, instance(list)) and len(arraydef) > 0:
# arraydef contains a list
# calc size recursive by sum of all elements
for i in range(0, arraydef[0]):
@@ -2162,7 +2166,7 @@ def GetFieldLength(fielddef):
else:
length += GetFieldLength( (format_, addrdef, None) )
- elif isinstance(format_, dict):
+ elif isinstance(format_, instance(dict)):
# -> iterate through format
addr = None
setting = format_
@@ -2174,7 +2178,7 @@ def GetFieldLength(fielddef):
length += _len
# a simple value
- elif isinstance(format_, str):
+ elif isinstance(format_, instance(str)):
length = struct.calcsize(format_)
return length
@@ -2200,7 +2204,7 @@ def GetSubfieldDef(fielddef):
arraydef = None
# create new datadef
- if isinstance(datadef, tuple):
+ if isinstance(datadef, instance(tuple)):
if cmd is not None:
datadef = (arraydef, validate, cmd)
else:
@@ -2293,8 +2297,8 @@ def SetFieldValue(fielddef, dobj, addr, value):
format_, bits, bitshift = GetFieldDef(fielddef, fields='format_, bits, bitshift')
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)
+ if debug(args) >= 2:
+ 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):
@@ -2302,10 +2306,10 @@ def SetFieldValue(fielddef, dobj, addr, value):
maxunsigned = ((2**bitsize) - 1)
maxsigned = ((2**bitsize)>>1)-1
val = value & maxunsigned
- if isinstance(value,int) and value < 0 and val > maxsigned:
+ if isinstance(value,instance(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)
+ if debug(args) >= 3:
+ 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:
@@ -2316,8 +2320,8 @@ def SetFieldValue(fielddef, dobj, addr, value):
line=inspect.getlineno(inspect.currentframe()))
value >>= bitsize
else:
- if args.debug >= 3:
- print >> sys.stderr, "SetFieldValue(): String type - fielddef {}, addr 0x{:04x} value {} format_ {}".format(fielddef,addr,value,format_)
+ if debug(args) >= 3:
+ 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:
@@ -2349,7 +2353,7 @@ def GetField(dobj, fieldname, fielddef, raw=False, addroffset=0):
field mapping
"""
- if isinstance(dobj, bytearray):
+ if isinstance(dobj, instance((bytes,bytearray))):
dobj = str(dobj)
valuemapping = None
@@ -2362,7 +2366,7 @@ def GetField(dobj, fieldname, fielddef, raw=False, addroffset=0):
return valuemapping
# contains a integer list
- if isinstance(arraydef, list) and len(arraydef) > 0:
+ if isinstance(arraydef, instance(list)) and len(arraydef) > 0:
valuemapping = []
offset = 0
for i in range(0, arraydef[0]):
@@ -2374,7 +2378,7 @@ def GetField(dobj, fieldname, fielddef, raw=False, addroffset=0):
offset += length
# contains a dict
- elif isinstance(format_, dict):
+ elif isinstance(format_, instance(dict)):
mapping_value = {}
# -> iterate through format
for name in format_:
@@ -2386,7 +2390,7 @@ def GetField(dobj, fieldname, fielddef, raw=False, addroffset=0):
valuemapping = copy.deepcopy(mapping_value)
# a simple value
- elif isinstance(format_, (str, bool, int, float, long)):
+ elif isinstance(format_, instance((str, bool, int, float, long))):
if GetFieldLength(fielddef) != 0:
valuemapping = ReadWriteConverter(GetFieldValue(fielddef, dobj, baseaddr+addroffset), fielddef, read=True, raw=raw)
@@ -2426,12 +2430,12 @@ 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))
+ if debug(args) >= 2:
+ print("SetField(): Readonly '{}' using '{}'/{}{} @{} skipped".format(fieldname, format_, arraydef, bits, hex(baseaddr+addroffset)), file=sys.stderr)
return dobj
# contains a list
- if isinstance(arraydef, list) and len(arraydef) > 0:
+ if isinstance(arraydef, instance(list)) and len(arraydef) > 0:
offset = 0
if len(restore) > arraydef[0]:
exit(ExitCode.RESTORE_DATA_ERROR, "file '{sfile}', array '{sname}[{selem}]' exceeds max number of elements [{smax}]".format(sfile=filename, sname=fieldname, selem=len(restore), smax=arraydef[0]), type_=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe()))
@@ -2446,13 +2450,13 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""):
offset += length
# contains a dict
- elif isinstance(format_, dict):
+ elif isinstance(format_, instance(dict)):
for name in format_: # -> iterate through format
if name in restore:
dobj = SetField(dobj, name, format_[name], restore[name], addroffset=addroffset, filename=filename)
# a simple value
- elif isinstance(format_, (str, bool, int, float, long)):
+ elif isinstance(format_, instance((str, bool, int, float, long))):
valid = True
err = ""
errformat = ""
@@ -2465,22 +2469,22 @@ 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
elif format_[-1:] in ['b','B','h','H','i','I','l','L','q','Q','P']:
value = ReadWriteConverter(restore, fielddef, read=False)
- if isinstance(value, (str, unicode)):
+ if isinstance(value, instance(str)):
value = int(value, 0)
else:
value = int(value)
@@ -2552,15 +2556,15 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""):
# copy value before possible change below
_value = value
- if isinstance(_value, (str, unicode)):
+ if isinstance(_value, instance(str)):
_value = "'{}'".format(_value)
if valid:
if not skip:
- if args.debug >= 2:
+ if debug(args) >= 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)
+ strvalue = "{} [{}]".format(_value, hex(value)) if isinstance(_value, instance(int)) else _value
+ 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)
@@ -2568,8 +2572,8 @@ def SetField(dobj, fieldname, fielddef, restore, addroffset=0, filename=""):
if prevvalue != curvalue and args.verbose:
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))
+ if debug(args) >= 2:
+ 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)
@@ -2609,7 +2613,7 @@ def SetCmnd(cmnds, fieldname, fielddef, valuemapping, mappedvalue, addroffset=0,
return cmnds
# contains a list
- if isinstance(arraydef, list) and len(arraydef) > 0:
+ if isinstance(arraydef, instance(list)) and len(arraydef) > 0:
offset = 0
if len(mappedvalue) > arraydef[0]:
exit(ExitCode.RESTORE_DATA_ERROR, "array '{sname}[{selem}]' exceeds max number of elements [{smax}]".format(sname=fieldname, selem=len(mappedvalue), smax=arraydef[0]), type_=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe()))
@@ -2624,23 +2628,23 @@ def SetCmnd(cmnds, fieldname, fielddef, valuemapping, mappedvalue, addroffset=0,
offset += length
# contains a dict
- elif isinstance(format_, dict):
+ elif isinstance(format_, instance(dict)):
for name in format_: # -> iterate through format
if name in mappedvalue:
cmnds = SetCmnd(cmnds, name, format_[name], valuemapping, mappedvalue[name], addroffset=addroffset, idx=idx)
# a simple value
- elif isinstance(format_, (str, bool, int, float, long)):
+ elif isinstance(format_, instance((str, bool, int, float, long))):
if group is not None:
group = group.title();
- if isinstance(tasmotacmnd, tuple):
+ if isinstance(tasmotacmnd, instance(tuple)):
tasmotacmnds = tasmotacmnd
for tasmotacmnd in tasmotacmnds:
cmnd = CmndConverter(valuemapping, mappedvalue, idx, fielddef)
if group is not None and cmnd is not None:
if group not in cmnds:
cmnds[group] = []
- if isinstance(cmnd, list):
+ if isinstance(cmnd, instance(list)):
for c in cmnd:
cmnds[group].append(c)
else:
@@ -2650,7 +2654,7 @@ def SetCmnd(cmnds, fieldname, fielddef, valuemapping, mappedvalue, addroffset=0,
if group is not None and cmnd is not None:
if group not in cmnds:
cmnds[group] = []
- if isinstance(cmnd, list):
+ if isinstance(cmnd, instance(list)):
for c in cmnd:
cmnds[group].append(c)
else:
@@ -2669,7 +2673,7 @@ def Bin2Mapping(decode_cfg):
@return:
valuemapping data as mapping dictionary
"""
- if isinstance(decode_cfg, bytearray):
+ if isinstance(decode_cfg, instance((bytes,bytearray))):
decode_cfg = str(decode_cfg)
# get binary header and template to use
@@ -2761,7 +2765,7 @@ def Mapping2Bin(decode_cfg, jsonconfig, filename=""):
@return:
changed binary config data (decrypted) or None on error
"""
- if isinstance(decode_cfg, str):
+ if isinstance(decode_cfg, instance(str)):
decode_cfg = bytearray(decode_cfg)
@@ -2811,7 +2815,7 @@ def Mapping2Cmnd(decode_cfg, valuemapping, filename=""):
@return:
Tasmota command mapping {group: [cmnd <,cmnd <,...>>]}
"""
- if isinstance(decode_cfg, str):
+ if isinstance(decode_cfg, instance(str)):
decode_cfg = bytearray(decode_cfg)
# get binary header data to use the correct version template from device
@@ -2871,8 +2875,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 +2888,8 @@ def Backup(backupfile, backupfileformat, encode_cfg, decode_cfg, configmapping):
with open(backup_filename, "wb") as backupfp:
backupfp.write(struct.pack('= 1:
- print >> sys.stderr, parser.format_values()
- print >> sys.stderr, "Settings:"
+ if debug(args) >= 1:
+ 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 +3284,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 +3313,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)