2018-09-25 13:08:36 +01:00
#!/usr/bin/env python
2018-10-02 12:42:21 +01:00
#!/usr/bin/env python
2018-09-25 13:08:36 +01:00
# -*- coding: utf-8 -*-
2018-10-14 12:18:16 +01:00
VER = ' 1.5.0013 '
2018-09-25 13:08:36 +01:00
"""
decode - config . py - Decode configuration of Sonoff - Tasmota device
Copyright ( C ) 2018 Norbert Richter < nr @prsolution.eu >
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : / / www . gnu . org / licenses / > .
2018-09-29 12:37:42 +01:00
2018-09-25 13:08:36 +01:00
Requirements :
- Python
2018-09-26 14:18:01 +01:00
- pip install json pycurl urllib2 configargparse
2018-09-25 13:08:36 +01:00
2018-09-29 12:37:42 +01:00
2018-09-25 13:08:36 +01:00
Instructions :
2018-09-29 12:37:42 +01:00
Execute command with option - d to retrieve config data from a host
or use - f to read out a configuration file saved using Tasmota Web - UI
2018-09-25 13:08:36 +01:00
For help execute command with argument - h
Usage :
2018-09-26 14:18:01 +01:00
decode - config . py [ - h ] [ - f < filename > ] [ - d < host > ] [ - u < user > ]
2018-09-29 12:37:42 +01:00
[ - p < password > ] [ - - json - indent < integer > ]
2018-10-02 12:42:21 +01:00
[ - - json - compact ] [ - - sort ] [ - - unsort ] [ - - raw - values ]
[ - - no - raw - values ] [ - - raw - keys ] [ - - no - raw - keys ]
[ - - hide - pw ] [ - - unhide - pw ] [ - o < filename > ]
[ - - output - file - format < word > ] [ - c < filename > ]
[ - - exit - on - error - only ] [ - V ]
2018-09-25 13:08:36 +01:00
Decode configuration of Sonoff - Tasmota device . Args that start with ' -- ' ( eg .
- f ) can also be set in a config file ( specified via - c ) . Config file syntax
allows : key = value , flag = true , stuff = [ a , b , c ] ( for details , see syntax at
https : / / goo . gl / R74nmi ) . If an arg is specified in more than one place , then
commandline values override config file values which override defaults .
optional arguments :
- h , - - help show this help message and exit
- c < filename > , - - config < filename >
Config file , can be used instead of command parameter
2018-09-26 14:18:01 +01:00
( default : None )
2018-09-29 12:37:42 +01:00
- - exit - on - error - only exit on error only ( default : exit on ERROR and
WARNING ) . Not recommended , used by your own
responsibility !
2018-09-25 13:08:36 +01:00
source :
- f < filename > , - - file < filename >
file to retrieve Tasmota configuration from ( default :
2018-09-29 12:37:42 +01:00
None ) '
2018-09-26 14:18:01 +01:00
- d < host > , - - device < host >
hostname or IP address to retrieve Tasmota
configuration from ( default : None )
2018-09-25 13:08:36 +01:00
- u < user > , - - username < user >
2018-09-29 12:37:42 +01:00
host HTTP access username ( default : admin )
2018-09-25 13:08:36 +01:00
- p < password > , - - password < password >
2018-09-29 12:37:42 +01:00
host HTTP access password ( default : None )
2018-09-25 13:08:36 +01:00
2018-10-02 12:42:21 +01:00
config :
2018-09-26 14:18:01 +01:00
- - json - indent < integer >
pretty - printed JSON output using indent level
2018-10-02 12:42:21 +01:00
( default : ' None ' ) . Use values greater equal 0 to
indent or - 1 to disabled indent .
- - json - compact compact JSON output by eliminate whitespace
- - sort sort json keywords ( default )
- - unsort do not sort json keywords
- - raw - values , - - raw output raw values
- - no - raw - values output human readable values ( default )
- - raw - keys output bitfield raw keys ( default )
- - no - raw - keys do not output bitfield raw keys
- - hide - pw hide passwords ( default )
- - unhide - pw unhide passwords
2018-09-25 13:08:36 +01:00
- o < filename > , - - output - file < filename >
2018-10-02 12:42:21 +01:00
file to store configuration to ( default : None ) .
Replacements : @v = Tasmota version , @f = friendly name
2018-09-29 12:37:42 +01:00
- - output - file - format < word >
output format ( ' json ' or ' binary ' , default : ' json ' )
2018-09-25 13:08:36 +01:00
info :
- V , - - version show program ' s version number and exit
2018-09-26 14:18:01 +01:00
Either argument - d < host > or - f < filename > must be given .
2018-09-25 13:08:36 +01:00
2018-10-02 12:42:21 +01:00
Returns :
0 : successful
1 : file not found
2 : configuration version not supported
3 : data size mismatch
4 : data CRC error
5 : configuration file read error
6 : argument error
9 : python module is missing
4 xx , 5 xx : HTTP error
2018-09-25 13:08:36 +01:00
"""
import os . path
import io
import sys
2018-10-02 12:42:21 +01:00
def ModuleImportError ( module ) :
er = str ( module )
print ( " {} . Try ' pip install {} ' to install it " . format ( er , er . split ( ' ' ) [ len ( er . split ( ' ' ) ) - 1 ] ) )
2018-09-29 12:37:42 +01:00
sys . exit ( 9 )
try :
2018-10-02 12:42:21 +01:00
import struct
import re
import math
from datetime import datetime
import json
2018-09-29 12:37:42 +01:00
import configargparse
2018-09-25 13:08:36 +01:00
import pycurl
import urllib2
2018-10-02 12:42:21 +01:00
except ImportError , e :
ModuleImportError ( e )
2018-09-25 13:08:36 +01:00
PROG = ' {} v {} by Norbert Richter ' . format ( os . path . basename ( sys . argv [ 0 ] ) , VER )
CONFIG_FILE_XOR = 0x5A
2018-10-02 12:42:21 +01:00
BINARYFILE_MAGIC = 0x63576223
2018-09-25 13:08:36 +01:00
args = { }
DEFAULTS = {
' DEFAULT ' :
{
' configfile ' : None ,
2018-09-29 12:37:42 +01:00
' exitonwarning ' : True ,
2018-09-25 13:08:36 +01:00
} ,
' source ' :
{
' device ' : None ,
' username ' : ' admin ' ,
' password ' : None ,
' tasmotafile ' : None ,
} ,
2018-10-02 12:42:21 +01:00
' config ' :
2018-09-25 13:08:36 +01:00
{
2018-09-26 14:18:01 +01:00
' jsonindent ' : None ,
' jsoncompact ' : False ,
2018-10-02 12:42:21 +01:00
' sort ' : True ,
' rawvalues ' : False ,
' rawkeys ' : True ,
' hidepw ' : True ,
2018-09-25 13:08:36 +01:00
' outputfile ' : None ,
2018-09-29 12:37:42 +01:00
' outputfileformat ' : ' json ' ,
2018-09-25 13:08:36 +01:00
} ,
}
2018-10-02 12:42:21 +01:00
exitcode = 0
"""
Settings dictionary describes the config file fields definition :
Each setting name has a tuple containing the following items :
( format , baseaddr , datadef , < convert > )
where
format
Define the data interpretation .
It is either a string or a tuple containing a string and a
sub - Settings dictionary .
' xxx ' :
A string is used to interpret the data at < baseaddr >
The string defines the format interpretion as described
in ' struct module format string ' , see
https : / / docs . python . org / 2.7 / library / struct . html #format-strings
In addition to this format string there is as special
meaning of a dot ' . ' - this means a bit with an optional
prefix length . If no prefix is given , 1 is assumed .
{ } :
A dictionary describes itself a ' Settings ' dictonary ( recursive )
baseaddr
The address ( starting from 0 ) within config data .
For bit fields < baseaddr > must be a tuple .
n :
Defines a simple address < n > within config data .
< n > must be a positive integer .
( n , b , s ) :
A tuple defines a bit field :
< n >
is the address within config data ( integer )
< b >
how many bits are used ( positive integer )
< s >
bit shift < s > ( integer )
positive < s > shift the result < s > right bits
negative < s > shift the result < s > left bits
datadef
Define the field interpretation different from simple
standard types ( like char , byte , int ) e . g . lists or bit fields
Can be None , a single integer , a list or a dictionary
None :
None must be given if the field contains a simple value
desrcibed by the < format > prefix
n :
Same as [ n ] below
[ n ] :
Defines a one - dimensional array of size < n >
[ n , n < , n . . . > ]
Defines a multi - dimensional array
convert ( optional )
Define an output / conversion methode , can be a simple string
or a previously defined function name .
' xxx? ' :
a string will be evaluate as is replacing all ' ? ' chars
with the current value . This can also be contain pyhton
code .
func :
a function defines the name of a formating function
"""
# config data conversion function and helper
def int2ip ( value ) :
return ' {:d} . {:d} . {:d} . {:d} ' . format ( value & 0xff , value >> 8 & 0xff , value >> 16 & 0xff , value >> 24 & 0xff )
def password ( value ) :
if args . hidepw :
return ' ******** '
return value
2018-10-14 12:18:16 +01:00
Setting_5_10_0 = {
' cfg_holder ' : ( ' <L ' , 0x000 , None ) ,
2018-10-02 12:42:21 +01:00
' save_flag ' : ( ' <L ' , 0x004 , None ) ,
2018-10-14 12:18:16 +01:00
' version ' : ( ' <L ' , 0x008 , None , ' " 0x {:x} " .format(?) ' ) ,
' bootcount ' : ( ' <L ' , 0x00C , None ) ,
2018-10-02 12:42:21 +01:00
' flag ' : ( {
' raw ' : ( ' <L ' , 0x010 , None , ' " 0x {:08x} " .format(?) ' ) ,
' save_state ' : ( ' <L ' , ( 0x010 , 1 , 0 ) , None ) ,
' button_restrict ' : ( ' <L ' , ( 0x010 , 1 , 1 ) , None ) ,
' value_units ' : ( ' <L ' , ( 0x010 , 1 , 2 ) , None ) ,
' mqtt_enabled ' : ( ' <L ' , ( 0x010 , 1 , 3 ) , None ) ,
' mqtt_response ' : ( ' <L ' , ( 0x010 , 1 , 4 ) , None ) ,
' mqtt_power_retain ' : ( ' <L ' , ( 0x010 , 1 , 5 ) , None ) ,
' mqtt_button_retain ' : ( ' <L ' , ( 0x010 , 1 , 6 ) , None ) ,
' mqtt_switch_retain ' : ( ' <L ' , ( 0x010 , 1 , 7 ) , None ) ,
' temperature_conversion ' : ( ' <L ' , ( 0x010 , 1 , 8 ) , None ) ,
' mqtt_sensor_retain ' : ( ' <L ' , ( 0x010 , 1 , 9 ) , None ) ,
' mqtt_offline ' : ( ' <L ' , ( 0x010 , 1 , 10 ) , None ) ,
' button_swap ' : ( ' <L ' , ( 0x010 , 1 , 11 ) , None ) ,
' stop_flash_rotate ' : ( ' <L ' , ( 0x010 , 1 , 12 ) , None ) ,
' button_single ' : ( ' <L ' , ( 0x010 , 1 , 13 ) , None ) ,
' interlock ' : ( ' <L ' , ( 0x010 , 1 , 14 ) , None ) ,
' pwm_control ' : ( ' <L ' , ( 0x010 , 1 , 15 ) , None ) ,
' ws_clock_reverse ' : ( ' <L ' , ( 0x010 , 1 , 16 ) , None ) ,
' decimal_text ' : ( ' <L ' , ( 0x010 , 1 , 17 ) , None ) ,
} , 0x010 , None ) ,
' save_data ' : ( ' <h ' , 0x014 , None ) ,
' timezone ' : ( ' b ' , 0x016 , None ) ,
' ota_url ' : ( ' 101s ' , 0x017 , None ) ,
' mqtt_prefix ' : ( ' 11s ' , 0x07C , [ 3 ] ) ,
' seriallog_level ' : ( ' B ' , 0x09E , None ) ,
' sta_config ' : ( ' B ' , 0x09F , None ) ,
' sta_active ' : ( ' B ' , 0x0A0 , None ) ,
' sta_ssid ' : ( ' 33s ' , 0x0A1 , [ 2 ] ) ,
' sta_pwd ' : ( ' 65s ' , 0x0E3 , [ 2 ] , password ) ,
' hostname ' : ( ' 33s ' , 0x165 , None ) ,
' syslog_host ' : ( ' 33s ' , 0x186 , None ) ,
' syslog_port ' : ( ' <H ' , 0x1A8 , None ) ,
' syslog_level ' : ( ' B ' , 0x1AA , None ) ,
' webserver ' : ( ' B ' , 0x1AB , None ) ,
' weblog_level ' : ( ' B ' , 0x1AC , None ) ,
2018-10-14 12:18:16 +01:00
' mqtt_fingerprint ' : ( ' 60s ' , 0x1AD , None ) ,
2018-10-02 12:42:21 +01:00
' mqtt_host ' : ( ' 33s ' , 0x1E9 , None ) ,
' mqtt_port ' : ( ' <H ' , 0x20A , None ) ,
' mqtt_client ' : ( ' 33s ' , 0x20C , None ) ,
' mqtt_user ' : ( ' 33s ' , 0x22D , None ) ,
' mqtt_pwd ' : ( ' 33s ' , 0x24E , None , password ) ,
' mqtt_topic ' : ( ' 33s ' , 0x26F , None ) ,
' button_topic ' : ( ' 33s ' , 0x290 , None ) ,
' mqtt_grptopic ' : ( ' 33s ' , 0x2B1 , None ) ,
2018-10-14 12:18:16 +01:00
' mqtt_fingerprinth ' : ( ' B ' , 0x2D2 , [ 20 ] ) ,
2018-10-02 12:42:21 +01:00
' pwm_frequency ' : ( ' <H ' , 0x2E6 , None ) ,
' power ' : ( {
' raw ' : ( ' <L ' , 0x2E8 , None , ' " 0x {:08x} " .format(?) ' ) ,
' power1 ' : ( ' <L ' , ( 0x2E8 , 1 , 0 ) , None ) ,
' power2 ' : ( ' <L ' , ( 0x2E8 , 1 , 1 ) , None ) ,
' power3 ' : ( ' <L ' , ( 0x2E8 , 1 , 2 ) , None ) ,
' power4 ' : ( ' <L ' , ( 0x2E8 , 1 , 3 ) , None ) ,
' power5 ' : ( ' <L ' , ( 0x2E8 , 1 , 4 ) , None ) ,
' power6 ' : ( ' <L ' , ( 0x2E8 , 1 , 5 ) , None ) ,
' power7 ' : ( ' <L ' , ( 0x2E8 , 1 , 6 ) , None ) ,
' power8 ' : ( ' <L ' , ( 0x2E8 , 1 , 7 ) , None ) ,
} , 0x2E8 , None ) ,
' pwm_value ' : ( ' <H ' , 0x2EC , [ 5 ] ) ,
' altitude ' : ( ' <h ' , 0x2F6 , None ) ,
' tele_period ' : ( ' <H ' , 0x2F8 , None ) ,
' ledstate ' : ( ' B ' , 0x2FB , None ) ,
2018-10-14 12:18:16 +01:00
' param ' : ( ' B ' , 0x2FC , [ 23 ] ) ,
2018-10-02 12:42:21 +01:00
' state_text ' : ( ' 11s ' , 0x313 , [ 4 ] ) ,
' domoticz_update_timer ' : ( ' <H ' , 0x340 , None ) ,
' pwm_range ' : ( ' <H ' , 0x342 , None ) ,
' domoticz_relay_idx ' : ( ' <L ' , 0x344 , [ 4 ] ) ,
' domoticz_key_idx ' : ( ' <L ' , 0x354 , [ 4 ] ) ,
' energy_power_calibration ' : ( ' <L ' , 0x364 , None ) ,
' energy_voltage_calibration ' : ( ' <L ' , 0x368 , None ) ,
' energy_current_calibration ' : ( ' <L ' , 0x36C , None ) ,
' energy_kWhtoday ' : ( ' <L ' , 0x370 , None ) ,
' energy_kWhyesterday ' : ( ' <L ' , 0x374 , None ) ,
' energy_kWhdoy ' : ( ' <H ' , 0x378 , None ) ,
' energy_min_power ' : ( ' <H ' , 0x37A , None ) ,
' energy_max_power ' : ( ' <H ' , 0x37C , None ) ,
' energy_min_voltage ' : ( ' <H ' , 0x37E , None ) ,
' energy_max_voltage ' : ( ' <H ' , 0x380 , None ) ,
' energy_min_current ' : ( ' <H ' , 0x382 , None ) ,
' energy_max_current ' : ( ' <H ' , 0x384 , None ) ,
' energy_max_power_limit ' : ( ' <H ' , 0x386 , None ) ,
2018-10-14 12:18:16 +01:00
' energy_max_power_limit_hold ' : ( ' <H ' , 0x388 , None ) ,
' energy_max_power_limit_window ' : ( ' <H ' , 0x38A , None ) ,
' energy_max_power_safe_limit ' : ( ' <H ' , 0x38C , None ) ,
' energy_max_power_safe_limit_hold ' : ( ' <H ' , 0x38E , None ) ,
' energy_max_power_safe_limit_window ' : ( ' <H ' , 0x390 , None ) ,
2018-10-02 12:42:21 +01:00
' energy_max_energy ' : ( ' <H ' , 0x392 , None ) ,
' energy_max_energy_start ' : ( ' <H ' , 0x394 , None ) ,
' mqtt_retry ' : ( ' <H ' , 0x396 , None ) ,
' poweronstate ' : ( ' B ' , 0x398 , None ) ,
' last_module ' : ( ' B ' , 0x399 , None ) ,
' blinktime ' : ( ' <H ' , 0x39A , None ) ,
' blinkcount ' : ( ' <H ' , 0x39C , None ) ,
' friendlyname ' : ( ' 33s ' , 0x3AC , [ 4 ] ) ,
' switch_topic ' : ( ' 33s ' , 0x430 , None ) ,
' sleep ' : ( ' B ' , 0x453 , None ) ,
' domoticz_switch_idx ' : ( ' <H ' , 0x454 , [ 4 ] ) ,
' domoticz_sensor_idx ' : ( ' <H ' , 0x45C , [ 12 ] ) ,
' module ' : ( ' B ' , 0x474 , None ) ,
' ws_color ' : ( ' B ' , 0x475 , [ 4 , 3 ] ) ,
' ws_width ' : ( ' B ' , 0x481 , [ 3 ] ) ,
' my_gp ' : ( ' B ' , 0x484 , [ 18 ] ) ,
' light_pixels ' : ( ' <H ' , 0x496 , None ) ,
' light_color ' : ( ' B ' , 0x498 , [ 5 ] ) ,
' light_correction ' : ( ' B ' , 0x49D , None ) ,
' light_dimmer ' : ( ' B ' , 0x49E , None ) ,
' light_fade ' : ( ' B ' , 0x4A1 , None ) ,
' light_speed ' : ( ' B ' , 0x4A2 , None ) ,
' light_scheme ' : ( ' B ' , 0x4A3 , None ) ,
' light_width ' : ( ' B ' , 0x4A4 , None ) ,
' light_wakeup ' : ( ' <H ' , 0x4A6 , None ) ,
' web_password ' : ( ' 33s ' , 0x4A9 , None , password ) ,
2018-10-14 12:18:16 +01:00
' switchmode ' : ( ' B ' , 0x4CA , [ 4 ] ) ,
2018-10-02 12:42:21 +01:00
' ntp_server ' : ( ' 33s ' , 0x4CE , [ 3 ] ) ,
' ina219_mode ' : ( ' B ' , 0x531 , None ) ,
' pulse_timer ' : ( ' <H ' , 0x532 , [ 8 ] ) ,
' ip_address ' : ( ' <L ' , 0x544 , [ 4 ] , int2ip ) ,
' energy_kWhtotal ' : ( ' <L ' , 0x554 , None ) ,
' mqtt_fulltopic ' : ( ' 100s ' , 0x558 , None ) ,
' flag2 ' : ( {
' raw ' : ( ' <L ' , 0x5BC , None , ' " 0x {:08x} " .format(?) ' ) ,
' current_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 15 ) , None ) ,
' voltage_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 17 ) , None ) ,
' wattage_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 19 ) , None ) ,
' emulation ' : ( ' <L ' , ( 0x5BC , 2 , 21 ) , None ) ,
' energy_resolution ' : ( ' <L ' , ( 0x5BC , 3 , 23 ) , None ) ,
' pressure_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 26 ) , None ) ,
' humidity_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 28 ) , None ) ,
' temperature_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 30 ) , None ) ,
} , 0x5BC , None ) ,
' pulse_counter ' : ( ' <L ' , 0x5C0 , [ 4 ] ) ,
' pulse_counter_type ' : ( ' <H ' , 0x5D0 , None ) ,
' pulse_counter_debounce ' : ( ' <H ' , 0x5D2 , None ) ,
' rf_code ' : ( ' B ' , 0x5D4 , [ 17 , 9 ] , ' " 0x {:02x} " .format(?) ' ) ,
2018-10-14 12:18:16 +01:00
}
Setting_5_11_0 = Setting_5_10_0
Setting_5_11_0 . update ( {
' flag ' : ( {
' raw ' : ( ' <L ' , 0x010 , None , ' " 0x {:08x} " .format(?) ' ) ,
' save_state ' : ( ' <L ' , ( 0x010 , 1 , 0 ) , None ) ,
' button_restrict ' : ( ' <L ' , ( 0x010 , 1 , 1 ) , None ) ,
' value_units ' : ( ' <L ' , ( 0x010 , 1 , 2 ) , None ) ,
' mqtt_enabled ' : ( ' <L ' , ( 0x010 , 1 , 3 ) , None ) ,
' mqtt_response ' : ( ' <L ' , ( 0x010 , 1 , 4 ) , None ) ,
' mqtt_power_retain ' : ( ' <L ' , ( 0x010 , 1 , 5 ) , None ) ,
' mqtt_button_retain ' : ( ' <L ' , ( 0x010 , 1 , 6 ) , None ) ,
' mqtt_switch_retain ' : ( ' <L ' , ( 0x010 , 1 , 7 ) , None ) ,
' temperature_conversion ' : ( ' <L ' , ( 0x010 , 1 , 8 ) , None ) ,
' mqtt_sensor_retain ' : ( ' <L ' , ( 0x010 , 1 , 9 ) , None ) ,
' mqtt_offline ' : ( ' <L ' , ( 0x010 , 1 , 10 ) , None ) ,
' button_swap ' : ( ' <L ' , ( 0x010 , 1 , 11 ) , None ) ,
' stop_flash_rotate ' : ( ' <L ' , ( 0x010 , 1 , 12 ) , None ) ,
' button_single ' : ( ' <L ' , ( 0x010 , 1 , 13 ) , None ) ,
' interlock ' : ( ' <L ' , ( 0x010 , 1 , 14 ) , None ) ,
' pwm_control ' : ( ' <L ' , ( 0x010 , 1 , 15 ) , None ) ,
' ws_clock_reverse ' : ( ' <L ' , ( 0x010 , 1 , 16 ) , None ) ,
' decimal_text ' : ( ' <L ' , ( 0x010 , 1 , 17 ) , None ) ,
' light_signal ' : ( ' <L ' , ( 0x010 , 1 , 18 ) , None ) ,
} , 0x010 , None ) ,
' display_model ' : ( ' B ' , 0x2D2 , None ) ,
' display_mode ' : ( ' B ' , 0x2D3 , None ) ,
' display_refresh ' : ( ' B ' , 0x2D4 , None ) ,
' display_rows ' : ( ' B ' , 0x2D5 , None ) ,
' display_cols ' : ( ' B ' , 0x2D6 , [ 2 ] ) ,
' display_address ' : ( ' B ' , 0x2D8 , [ 8 ] ) ,
' display_dimmer ' : ( ' B ' , 0x2E0 , None ) ,
' display_size ' : ( ' B ' , 0x2E1 , None ) ,
} )
del Setting_5_11_0 [ ' mqtt_fingerprinth ' ]
Setting_5_12_0 = Setting_5_11_0
Setting_5_12_0 . update ( {
' flag ' : ( {
' raw ' : ( ' <L ' , 0x010 , None , ' " 0x {:08x} " .format(?) ' ) ,
' save_state ' : ( ' <L ' , ( 0x010 , 1 , 0 ) , None ) ,
' button_restrict ' : ( ' <L ' , ( 0x010 , 1 , 1 ) , None ) ,
' value_units ' : ( ' <L ' , ( 0x010 , 1 , 2 ) , None ) ,
' mqtt_enabled ' : ( ' <L ' , ( 0x010 , 1 , 3 ) , None ) ,
' mqtt_response ' : ( ' <L ' , ( 0x010 , 1 , 4 ) , None ) ,
' mqtt_power_retain ' : ( ' <L ' , ( 0x010 , 1 , 5 ) , None ) ,
' mqtt_button_retain ' : ( ' <L ' , ( 0x010 , 1 , 6 ) , None ) ,
' mqtt_switch_retain ' : ( ' <L ' , ( 0x010 , 1 , 7 ) , None ) ,
' temperature_conversion ' : ( ' <L ' , ( 0x010 , 1 , 8 ) , None ) ,
' mqtt_sensor_retain ' : ( ' <L ' , ( 0x010 , 1 , 9 ) , None ) ,
' mqtt_offline ' : ( ' <L ' , ( 0x010 , 1 , 10 ) , None ) ,
' button_swap ' : ( ' <L ' , ( 0x010 , 1 , 11 ) , None ) ,
' stop_flash_rotate ' : ( ' <L ' , ( 0x010 , 1 , 12 ) , None ) ,
' button_single ' : ( ' <L ' , ( 0x010 , 1 , 13 ) , None ) ,
' interlock ' : ( ' <L ' , ( 0x010 , 1 , 14 ) , None ) ,
' pwm_control ' : ( ' <L ' , ( 0x010 , 1 , 15 ) , None ) ,
' ws_clock_reverse ' : ( ' <L ' , ( 0x010 , 1 , 16 ) , None ) ,
' decimal_text ' : ( ' <L ' , ( 0x010 , 1 , 17 ) , None ) ,
' light_signal ' : ( ' <L ' , ( 0x010 , 1 , 18 ) , None ) ,
' hass_discovery ' : ( ' <L ' , ( 0x010 , 1 , 19 ) , None ) ,
' not_power_linked ' : ( ' <L ' , ( 0x010 , 1 , 20 ) , None ) ,
' no_power_on_check ' : ( ' <L ' , ( 0x010 , 1 , 21 ) , None ) ,
} , 0x010 , None ) ,
} )
Setting_5_13_1 = Setting_5_12_0
Setting_5_13_1 . update ( {
' flag ' : ( {
' raw ' : ( ' <L ' , 0x010 , None , ' " 0x {:08x} " .format(?) ' ) ,
' save_state ' : ( ' <L ' , ( 0x010 , 1 , 0 ) , None ) ,
' button_restrict ' : ( ' <L ' , ( 0x010 , 1 , 1 ) , None ) ,
' value_units ' : ( ' <L ' , ( 0x010 , 1 , 2 ) , None ) ,
' mqtt_enabled ' : ( ' <L ' , ( 0x010 , 1 , 3 ) , None ) ,
' mqtt_response ' : ( ' <L ' , ( 0x010 , 1 , 4 ) , None ) ,
' mqtt_power_retain ' : ( ' <L ' , ( 0x010 , 1 , 5 ) , None ) ,
' mqtt_button_retain ' : ( ' <L ' , ( 0x010 , 1 , 6 ) , None ) ,
' mqtt_switch_retain ' : ( ' <L ' , ( 0x010 , 1 , 7 ) , None ) ,
' temperature_conversion ' : ( ' <L ' , ( 0x010 , 1 , 8 ) , None ) ,
' mqtt_sensor_retain ' : ( ' <L ' , ( 0x010 , 1 , 9 ) , None ) ,
' mqtt_offline ' : ( ' <L ' , ( 0x010 , 1 , 10 ) , None ) ,
' button_swap ' : ( ' <L ' , ( 0x010 , 1 , 11 ) , None ) ,
' stop_flash_rotate ' : ( ' <L ' , ( 0x010 , 1 , 12 ) , None ) ,
' button_single ' : ( ' <L ' , ( 0x010 , 1 , 13 ) , None ) ,
' interlock ' : ( ' <L ' , ( 0x010 , 1 , 14 ) , None ) ,
' pwm_control ' : ( ' <L ' , ( 0x010 , 1 , 15 ) , None ) ,
' ws_clock_reverse ' : ( ' <L ' , ( 0x010 , 1 , 16 ) , None ) ,
' decimal_text ' : ( ' <L ' , ( 0x010 , 1 , 17 ) , None ) ,
' light_signal ' : ( ' <L ' , ( 0x010 , 1 , 18 ) , None ) ,
' hass_discovery ' : ( ' <L ' , ( 0x010 , 1 , 19 ) , None ) ,
' not_power_linked ' : ( ' <L ' , ( 0x010 , 1 , 20 ) , None ) ,
' no_power_on_check ' : ( ' <L ' , ( 0x010 , 1 , 21 ) , None ) ,
' mqtt_serial ' : ( ' <L ' , ( 0x010 , 1 , 22 ) , None ) ,
' rules_enabled ' : ( ' <L ' , ( 0x010 , 1 , 23 ) , None ) ,
' rules_once ' : ( ' <L ' , ( 0x010 , 1 , 24 ) , None ) ,
' knx_enabled ' : ( ' <L ' , ( 0x010 , 1 , 25 ) , None ) ,
} , 0x010 , None ) ,
' baudrate ' : ( ' B ' , 0x09D , None , ' ? * 1200 ' ) ,
' mqtt_fingerprint ' : ( ' 20s ' , 0x1AD , [ 2 ] ) ,
' energy_power_delta ' : ( ' B ' , 0x33F , None ) ,
' light_rotation ' : ( ' <H ' , 0x39E , None ) ,
' serial_delimiter ' : ( ' B ' , 0x451 , None ) ,
' sbaudrate ' : ( ' B ' , 0x452 , None ) ,
' knx_GA_registered ' : ( ' B ' , 0x4A5 , None ) ,
' knx_CB_registered ' : ( ' B ' , 0x4A8 , None ) ,
2018-10-02 12:42:21 +01:00
' timer ' : ( {
' raw ' : ( ' <L ' , 0x670 , None , ' " 0x {:08x} " .format(?) ' ) ,
' time ' : ( ' <L ' , ( 0x670 , 11 , 0 ) , None ) ,
' window ' : ( ' <L ' , ( 0x670 , 4 , 11 ) , None ) ,
' repeat ' : ( ' <L ' , ( 0x670 , 1 , 15 ) , None ) ,
' days ' : ( ' <L ' , ( 0x670 , 7 , 16 ) , None ) ,
' device ' : ( ' <L ' , ( 0x670 , 4 , 23 ) , None ) ,
' power ' : ( ' <L ' , ( 0x670 , 2 , 27 ) , None ) ,
' mode ' : ( ' <L ' , ( 0x670 , 2 , 29 ) , None ) ,
' arm ' : ( ' <L ' , ( 0x670 , 1 , 31 ) , None ) ,
} , 0x670 , [ 16 ] ) ,
' latitude ' : ( ' i ' , 0x6B0 , None , ' float(?) / 1000000 ' ) ,
' longitude ' : ( ' i ' , 0x6B4 , None , ' float(?) / 1000000 ' ) ,
' knx_physsical_addr ' : ( ' <H ' , 0x6B8 , None ) ,
' knx_GA_addr ' : ( ' <H ' , 0x6BA , [ 10 ] ) ,
' knx_CB_addr ' : ( ' <H ' , 0x6CE , [ 10 ] ) ,
' knx_GA_param ' : ( ' B ' , 0x6E2 , [ 10 ] ) ,
' knx_CB_param ' : ( ' B ' , 0x6EC , [ 10 ] ) ,
2018-10-14 12:18:16 +01:00
' rules ' : ( ' 512s ' , 0x800 , None ) ,
} )
Setting_5_14_0 = Setting_5_13_1
Setting_5_14_0 . update ( {
2018-10-02 12:42:21 +01:00
' flag ' : ( {
' raw ' : ( ' <L ' , 0x010 , None , ' " 0x {:08x} " .format(?) ' ) ,
' save_state ' : ( ' <L ' , ( 0x010 , 1 , 0 ) , None ) ,
' button_restrict ' : ( ' <L ' , ( 0x010 , 1 , 1 ) , None ) ,
' value_units ' : ( ' <L ' , ( 0x010 , 1 , 2 ) , None ) ,
' mqtt_enabled ' : ( ' <L ' , ( 0x010 , 1 , 3 ) , None ) ,
' mqtt_response ' : ( ' <L ' , ( 0x010 , 1 , 4 ) , None ) ,
' mqtt_power_retain ' : ( ' <L ' , ( 0x010 , 1 , 5 ) , None ) ,
' mqtt_button_retain ' : ( ' <L ' , ( 0x010 , 1 , 6 ) , None ) ,
' mqtt_switch_retain ' : ( ' <L ' , ( 0x010 , 1 , 7 ) , None ) ,
' temperature_conversion ' : ( ' <L ' , ( 0x010 , 1 , 8 ) , None ) ,
' mqtt_sensor_retain ' : ( ' <L ' , ( 0x010 , 1 , 9 ) , None ) ,
' mqtt_offline ' : ( ' <L ' , ( 0x010 , 1 , 10 ) , None ) ,
' button_swap ' : ( ' <L ' , ( 0x010 , 1 , 11 ) , None ) ,
' stop_flash_rotate ' : ( ' <L ' , ( 0x010 , 1 , 12 ) , None ) ,
' button_single ' : ( ' <L ' , ( 0x010 , 1 , 13 ) , None ) ,
' interlock ' : ( ' <L ' , ( 0x010 , 1 , 14 ) , None ) ,
' pwm_control ' : ( ' <L ' , ( 0x010 , 1 , 15 ) , None ) ,
' ws_clock_reverse ' : ( ' <L ' , ( 0x010 , 1 , 16 ) , None ) ,
' decimal_text ' : ( ' <L ' , ( 0x010 , 1 , 17 ) , None ) ,
' light_signal ' : ( ' <L ' , ( 0x010 , 1 , 18 ) , None ) ,
' hass_discovery ' : ( ' <L ' , ( 0x010 , 1 , 19 ) , None ) ,
' not_power_linked ' : ( ' <L ' , ( 0x010 , 1 , 20 ) , None ) ,
' no_power_on_check ' : ( ' <L ' , ( 0x010 , 1 , 21 ) , None ) ,
' mqtt_serial ' : ( ' <L ' , ( 0x010 , 1 , 22 ) , None ) ,
' rules_enabled ' : ( ' <L ' , ( 0x010 , 1 , 23 ) , None ) ,
' rules_once ' : ( ' <L ' , ( 0x010 , 1 , 24 ) , None ) ,
' knx_enabled ' : ( ' <L ' , ( 0x010 , 1 , 25 ) , None ) ,
' device_index_enable ' : ( ' <L ' , ( 0x010 , 1 , 26 ) , None ) ,
} , 0x010 , None ) ,
' tflag ' : ( {
' raw ' : ( ' <H ' , 0x2E2 , None , ' " 0x {:04x} " .format(?) ' ) ,
' hemis ' : ( ' <H ' , ( 0x2E2 , 1 , 0 ) , None ) ,
' week ' : ( ' <H ' , ( 0x2E2 , 3 , 1 ) , None ) ,
' month ' : ( ' <H ' , ( 0x2E2 , 4 , 4 ) , None ) ,
' dow ' : ( ' <H ' , ( 0x2E2 , 3 , 8 ) , None ) ,
' hour ' : ( ' <H ' , ( 0x2E2 , 5 , 13 ) , None ) ,
} , 0x2E2 , [ 2 ] ) ,
' param ' : ( ' B ' , 0x2FC , [ 18 ] ) ,
' toffset ' : ( ' <h ' , 0x30E , [ 2 ] ) ,
2018-10-14 12:18:16 +01:00
} )
Setting_6_0_0 = Setting_5_14_0
Setting_6_0_0 . update ( {
' cfg_holder ' : ( ' <H ' , 0x000 , None ) ,
' cfg_size ' : ( ' <H ' , 0x002 , None ) ,
' bootcount ' : ( ' <H ' , 0x00C , None ) ,
' cfg_crc ' : ( ' <H ' , 0x00E , None ) ,
' flag ' : ( {
' raw ' : ( ' <L ' , 0x010 , None , ' " 0x {:08x} " .format(?) ' ) ,
' save_state ' : ( ' <L ' , ( 0x010 , 1 , 0 ) , None ) ,
' button_restrict ' : ( ' <L ' , ( 0x010 , 1 , 1 ) , None ) ,
' value_units ' : ( ' <L ' , ( 0x010 , 1 , 2 ) , None ) ,
' mqtt_enabled ' : ( ' <L ' , ( 0x010 , 1 , 3 ) , None ) ,
' mqtt_response ' : ( ' <L ' , ( 0x010 , 1 , 4 ) , None ) ,
' mqtt_power_retain ' : ( ' <L ' , ( 0x010 , 1 , 5 ) , None ) ,
' mqtt_button_retain ' : ( ' <L ' , ( 0x010 , 1 , 6 ) , None ) ,
' mqtt_switch_retain ' : ( ' <L ' , ( 0x010 , 1 , 7 ) , None ) ,
' temperature_conversion ' : ( ' <L ' , ( 0x010 , 1 , 8 ) , None ) ,
' mqtt_sensor_retain ' : ( ' <L ' , ( 0x010 , 1 , 9 ) , None ) ,
' mqtt_offline ' : ( ' <L ' , ( 0x010 , 1 , 10 ) , None ) ,
' button_swap ' : ( ' <L ' , ( 0x010 , 1 , 11 ) , None ) ,
' stop_flash_rotate ' : ( ' <L ' , ( 0x010 , 1 , 12 ) , None ) ,
' button_single ' : ( ' <L ' , ( 0x010 , 1 , 13 ) , None ) ,
' interlock ' : ( ' <L ' , ( 0x010 , 1 , 14 ) , None ) ,
' pwm_control ' : ( ' <L ' , ( 0x010 , 1 , 15 ) , None ) ,
' ws_clock_reverse ' : ( ' <L ' , ( 0x010 , 1 , 16 ) , None ) ,
' decimal_text ' : ( ' <L ' , ( 0x010 , 1 , 17 ) , None ) ,
' light_signal ' : ( ' <L ' , ( 0x010 , 1 , 18 ) , None ) ,
' hass_discovery ' : ( ' <L ' , ( 0x010 , 1 , 19 ) , None ) ,
' not_power_linked ' : ( ' <L ' , ( 0x010 , 1 , 20 ) , None ) ,
' no_power_on_check ' : ( ' <L ' , ( 0x010 , 1 , 21 ) , None ) ,
' mqtt_serial ' : ( ' <L ' , ( 0x010 , 1 , 22 ) , None ) ,
' rules_enabled ' : ( ' <L ' , ( 0x010 , 1 , 23 ) , None ) ,
' rules_once ' : ( ' <L ' , ( 0x010 , 1 , 24 ) , None ) ,
' knx_enabled ' : ( ' <L ' , ( 0x010 , 1 , 25 ) , None ) ,
' device_index_enable ' : ( ' <L ' , ( 0x010 , 1 , 26 ) , None ) ,
' knx_enable_enhancement ' : ( ' <L ' , ( 0x010 , 1 , 27 ) , None ) ,
} , 0x010 , None ) ,
2018-10-02 12:42:21 +01:00
' rule_enabled ' : ( {
' raw ' : ( ' B ' , 0x49F , None ) ,
' rule1 ' : ( ' B ' , ( 0x49F , 1 , 0 ) , None ) ,
' rule2 ' : ( ' B ' , ( 0x49F , 1 , 1 ) , None ) ,
' rule3 ' : ( ' B ' , ( 0x49F , 1 , 2 ) , None ) ,
} , 0x49F , None ) ,
' rule_once ' : ( {
' raw ' : ( ' B ' , 0x4A0 , None ) ,
' rule1 ' : ( ' B ' , ( 0x4A0 , 1 , 0 ) , None , ' ? + 4 ' ) ,
' rule2 ' : ( ' B ' , ( 0x4A0 , 1 , 1 ) , None , ' ? + 4 ' ) ,
' rule3 ' : ( ' B ' , ( 0x4A0 , 1 , 2 ) , None , ' ? + 4 ' ) ,
} , 0x4A0 , None ) ,
2018-10-14 12:18:16 +01:00
' mems ' : ( ' 10s ' , 0x7CE , [ 5 ] ) ,
' rules ' : ( ' 512s ' , 0x800 , [ 3 ] )
} )
Setting_6_1_1 = Setting_6_0_0
Setting_6_1_1 . update ( {
' flag ' : ( {
' raw ' : ( ' <L ' , 0x010 , None , ' " 0x {:08x} " .format(?) ' ) ,
' save_state ' : ( ' <L ' , ( 0x010 , 1 , 0 ) , None ) ,
' button_restrict ' : ( ' <L ' , ( 0x010 , 1 , 1 ) , None ) ,
' value_units ' : ( ' <L ' , ( 0x010 , 1 , 2 ) , None ) ,
' mqtt_enabled ' : ( ' <L ' , ( 0x010 , 1 , 3 ) , None ) ,
' mqtt_response ' : ( ' <L ' , ( 0x010 , 1 , 4 ) , None ) ,
' mqtt_power_retain ' : ( ' <L ' , ( 0x010 , 1 , 5 ) , None ) ,
' mqtt_button_retain ' : ( ' <L ' , ( 0x010 , 1 , 6 ) , None ) ,
' mqtt_switch_retain ' : ( ' <L ' , ( 0x010 , 1 , 7 ) , None ) ,
' temperature_conversion ' : ( ' <L ' , ( 0x010 , 1 , 8 ) , None ) ,
' mqtt_sensor_retain ' : ( ' <L ' , ( 0x010 , 1 , 9 ) , None ) ,
' mqtt_offline ' : ( ' <L ' , ( 0x010 , 1 , 10 ) , None ) ,
' button_swap ' : ( ' <L ' , ( 0x010 , 1 , 11 ) , None ) ,
' stop_flash_rotate ' : ( ' <L ' , ( 0x010 , 1 , 12 ) , None ) ,
' button_single ' : ( ' <L ' , ( 0x010 , 1 , 13 ) , None ) ,
' interlock ' : ( ' <L ' , ( 0x010 , 1 , 14 ) , None ) ,
' pwm_control ' : ( ' <L ' , ( 0x010 , 1 , 15 ) , None ) ,
' ws_clock_reverse ' : ( ' <L ' , ( 0x010 , 1 , 16 ) , None ) ,
' decimal_text ' : ( ' <L ' , ( 0x010 , 1 , 17 ) , None ) ,
' light_signal ' : ( ' <L ' , ( 0x010 , 1 , 18 ) , None ) ,
' hass_discovery ' : ( ' <L ' , ( 0x010 , 1 , 19 ) , None ) ,
' not_power_linked ' : ( ' <L ' , ( 0x010 , 1 , 20 ) , None ) ,
' no_power_on_check ' : ( ' <L ' , ( 0x010 , 1 , 21 ) , None ) ,
' mqtt_serial ' : ( ' <L ' , ( 0x010 , 1 , 22 ) , None ) ,
' rules_enabled ' : ( ' <L ' , ( 0x010 , 1 , 23 ) , None ) ,
' rules_once ' : ( ' <L ' , ( 0x010 , 1 , 24 ) , None ) ,
' knx_enabled ' : ( ' <L ' , ( 0x010 , 1 , 25 ) , None ) ,
' device_index_enable ' : ( ' <L ' , ( 0x010 , 1 , 26 ) , None ) ,
' knx_enable_enhancement ' : ( ' <L ' , ( 0x010 , 1 , 27 ) , None ) ,
' rf_receive_decimal ' : ( ' <L ' , ( 0x010 , 1 , 28 ) , None ) ,
' ir_receive_decimal ' : ( ' <L ' , ( 0x010 , 1 , 29 ) , None ) ,
' hass_light ' : ( ' <L ' , ( 0x010 , 1 , 30 ) , None ) ,
} , 0x010 , None ) ,
' flag3 ' : ( ' <L ' , 0x3A0 , None , ' " 0x {:08x} " .format(?) ' ) ,
' switchmode ' : ( ' B ' , 0x3A4 , [ 8 ] ) ,
2018-10-02 12:42:21 +01:00
' mcp230xx_config ' : ( {
' raw ' : ( ' <L ' , 0x6F6 , None , ' " 0x {:08x} " .format(?) ' ) ,
' pinmode ' : ( ' <L ' , ( 0x6F6 , 3 , 0 ) , None ) ,
' pullup ' : ( ' <L ' , ( 0x6F6 , 1 , 3 ) , None ) ,
' saved_state ' : ( ' <L ' , ( 0x6F6 , 1 , 4 ) , None ) ,
' int_report_mode ' : ( ' <L ' , ( 0x6F6 , 2 , 5 ) , None ) ,
' int_report_defer ' : ( ' <L ' , ( 0x6F6 , 4 , 7 ) , None ) ,
' int_count_en ' : ( ' <L ' , ( 0x6F6 , 1 , 11 ) , None ) ,
} , 0x6F6 , [ 16 ] ) ,
2018-10-14 12:18:16 +01:00
} )
Setting_6_2_1 = Setting_6_1_1
Setting_6_2_1 . update ( {
2018-10-02 12:42:21 +01:00
' flag ' : ( {
' raw ' : ( ' <L ' , 0x010 , None , ' " 0x {:08x} " .format(?) ' ) ,
' save_state ' : ( ' <L ' , ( 0x010 , 1 , 0 ) , None ) ,
' button_restrict ' : ( ' <L ' , ( 0x010 , 1 , 1 ) , None ) ,
' value_units ' : ( ' <L ' , ( 0x010 , 1 , 2 ) , None ) ,
' mqtt_enabled ' : ( ' <L ' , ( 0x010 , 1 , 3 ) , None ) ,
' mqtt_response ' : ( ' <L ' , ( 0x010 , 1 , 4 ) , None ) ,
' mqtt_power_retain ' : ( ' <L ' , ( 0x010 , 1 , 5 ) , None ) ,
' mqtt_button_retain ' : ( ' <L ' , ( 0x010 , 1 , 6 ) , None ) ,
' mqtt_switch_retain ' : ( ' <L ' , ( 0x010 , 1 , 7 ) , None ) ,
' temperature_conversion ' : ( ' <L ' , ( 0x010 , 1 , 8 ) , None ) ,
' mqtt_sensor_retain ' : ( ' <L ' , ( 0x010 , 1 , 9 ) , None ) ,
' mqtt_offline ' : ( ' <L ' , ( 0x010 , 1 , 10 ) , None ) ,
' button_swap ' : ( ' <L ' , ( 0x010 , 1 , 11 ) , None ) ,
' stop_flash_rotate ' : ( ' <L ' , ( 0x010 , 1 , 12 ) , None ) ,
' button_single ' : ( ' <L ' , ( 0x010 , 1 , 13 ) , None ) ,
' interlock ' : ( ' <L ' , ( 0x010 , 1 , 14 ) , None ) ,
' pwm_control ' : ( ' <L ' , ( 0x010 , 1 , 15 ) , None ) ,
' ws_clock_reverse ' : ( ' <L ' , ( 0x010 , 1 , 16 ) , None ) ,
' decimal_text ' : ( ' <L ' , ( 0x010 , 1 , 17 ) , None ) ,
' light_signal ' : ( ' <L ' , ( 0x010 , 1 , 18 ) , None ) ,
' hass_discovery ' : ( ' <L ' , ( 0x010 , 1 , 19 ) , None ) ,
' not_power_linked ' : ( ' <L ' , ( 0x010 , 1 , 20 ) , None ) ,
' no_power_on_check ' : ( ' <L ' , ( 0x010 , 1 , 21 ) , None ) ,
' mqtt_serial ' : ( ' <L ' , ( 0x010 , 1 , 22 ) , None ) ,
' rules_enabled ' : ( ' <L ' , ( 0x010 , 1 , 23 ) , None ) ,
' rules_once ' : ( ' <L ' , ( 0x010 , 1 , 24 ) , None ) ,
' knx_enabled ' : ( ' <L ' , ( 0x010 , 1 , 25 ) , None ) ,
' device_index_enable ' : ( ' <L ' , ( 0x010 , 1 , 26 ) , None ) ,
' knx_enable_enhancement ' : ( ' <L ' , ( 0x010 , 1 , 27 ) , None ) ,
' rf_receive_decimal ' : ( ' <L ' , ( 0x010 , 1 , 28 ) , None ) ,
' ir_receive_decimal ' : ( ' <L ' , ( 0x010 , 1 , 29 ) , None ) ,
' hass_light ' : ( ' <L ' , ( 0x010 , 1 , 30 ) , None ) ,
' global_state ' : ( ' <L ' , ( 0x010 , 1 , 31 ) , None ) ,
} , 0x010 , None ) ,
' rule_stop ' : ( {
' raw ' : ( ' B ' , 0x1A7 , None ) ,
' rule1 ' : ( ' B ' , ( 0x1A7 , 1 , 0 ) , None , ' ? + 8 ' ) ,
' rule2 ' : ( ' B ' , ( 0x1A7 , 1 , 1 ) , None , ' ? + 8 ' ) ,
' rule3 ' : ( ' B ' , ( 0x1A7 , 1 , 2 ) , None , ' ? + 8 ' ) ,
} , 0x1A7 , None ) ,
' display_rotate ' : ( ' B ' , 0x2FA , None ) ,
' display_font ' : ( ' B ' , 0x312 , None ) ,
' flag3 ' : ( {
' raw ' : ( ' <L ' , 0x3A0 , None , ' " 0x {:08x} " .format(?) ' ) ,
' timers_enable ' : ( ' <L ' , ( 0x3A0 , 1 , 0 ) , None ) ,
2018-10-14 12:18:16 +01:00
' user_esp8285_enable ' : ( ' <L ' , ( 0x3A0 , 1 , 31 ) , None ) ,
2018-10-02 12:42:21 +01:00
} , 0x3A0 , None ) ,
' button_debounce ' : ( ' <H ' , 0x542 , None ) ,
' flag2 ' : ( {
' raw ' : ( ' <L ' , 0x5BC , None , ' " 0x {:08x} " .format(?) ' ) ,
' axis_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 13 ) , None ) ,
' current_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 15 ) , None ) ,
' voltage_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 17 ) , None ) ,
' wattage_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 19 ) , None ) ,
' emulation ' : ( ' <L ' , ( 0x5BC , 2 , 21 ) , None ) ,
' energy_resolution ' : ( ' <L ' , ( 0x5BC , 3 , 23 ) , None ) ,
' pressure_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 26 ) , None ) ,
' humidity_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 28 ) , None ) ,
' temperature_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 30 ) , None ) ,
} , 0x5BC , None ) ,
' switch_debounce ' : ( ' <H ' , 0x66E , None ) ,
' mcp230xx_int_prio ' : ( ' B ' , 0x716 , None ) ,
' mcp230xx_int_timer ' : ( ' <H ' , 0x718 , None ) ,
2018-10-14 12:18:16 +01:00
} )
Setting_6_2_1_2 = Setting_6_2_1
Setting_6_2_1_2 . update ( {
2018-10-02 12:42:21 +01:00
' flag3 ' : ( {
' raw ' : ( ' <L ' , 0x3A0 , None , ' " 0x {:08x} " .format(?) ' ) ,
' timers_enable ' : ( ' <L ' , ( 0x3A0 , 1 , 0 ) , None ) ,
' user_esp8285_enable ' : ( ' <L ' , ( 0x3A0 , 1 , 1 ) , None ) ,
} , 0x3A0 , None ) ,
2018-10-14 12:18:16 +01:00
} )
Setting_6_2_1_3 = Setting_6_2_1_2
Setting_6_2_1_3 . update ( {
' flag3 ' : ( {
' raw ' : ( ' <L ' , 0x3A0 , None , ' " 0x {:08x} " .format(?) ' ) ,
' timers_enable ' : ( ' <L ' , ( 0x3A0 , 1 , 0 ) , None ) ,
' user_esp8285_enable ' : ( ' <L ' , ( 0x3A0 , 1 , 1 ) , None ) ,
' time_append_timezone ' : ( ' <L ' , ( 0x3A0 , 1 , 2 ) , None ) ,
} , 0x3A0 , None ) ,
2018-09-29 12:37:42 +01:00
' flag2 ' : ( {
' raw ' : ( ' <L ' , 0x5BC , None , ' " 0x {:08x} " .format(?) ' ) ,
2018-10-14 12:18:16 +01:00
' frequency_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 11 ) , None ) ,
' axis_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 13 ) , None ) ,
2018-09-29 12:37:42 +01:00
' current_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 15 ) , None ) ,
' voltage_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 17 ) , None ) ,
' wattage_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 19 ) , None ) ,
' emulation ' : ( ' <L ' , ( 0x5BC , 2 , 21 ) , None ) ,
' energy_resolution ' : ( ' <L ' , ( 0x5BC , 3 , 23 ) , None ) ,
' pressure_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 26 ) , None ) ,
' humidity_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 28 ) , None ) ,
' temperature_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 30 ) , None ) ,
} , 0x5BC , None ) ,
2018-10-14 12:18:16 +01:00
} )
Setting_6_2_1_6 = Setting_6_2_1_3
Setting_6_2_1_6 . update ( {
' energy_frequency_calibration ' : ( ' <L ' , 0x7C8 , None ) ,
} )
Setting_6_2_1_10 = Setting_6_2_1_6
Setting_6_2_1_10 . update ( {
' rgbwwTable ' : ( ' B ' , 0x71A , [ 5 ] ) ,
} )
Setting_6_2_1_14 = Setting_6_2_1_10
Setting_6_2_1_14 . update ( {
2018-09-29 12:37:42 +01:00
' flag2 ' : ( {
' raw ' : ( ' <L ' , 0x5BC , None , ' " 0x {:08x} " .format(?) ' ) ,
2018-10-14 12:18:16 +01:00
' weight_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 9 ) , None ) ,
' frequency_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 11 ) , None ) ,
' axis_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 13 ) , None ) ,
2018-09-29 12:37:42 +01:00
' current_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 15 ) , None ) ,
' voltage_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 17 ) , None ) ,
' wattage_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 19 ) , None ) ,
' emulation ' : ( ' <L ' , ( 0x5BC , 2 , 21 ) , None ) ,
' energy_resolution ' : ( ' <L ' , ( 0x5BC , 3 , 23 ) , None ) ,
' pressure_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 26 ) , None ) ,
' humidity_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 28 ) , None ) ,
' temperature_resolution ' : ( ' <L ' , ( 0x5BC , 2 , 30 ) , None ) ,
} , 0x5BC , None ) ,
2018-10-14 12:18:16 +01:00
' weight_item ' : ( ' <H ' , 0x7BC , None ) ,
' weight_max ' : ( ' <H ' , 0x7BE , None ) ,
' weight_reference ' : ( ' <L ' , 0x7C0 , None ) ,
' weight_calibration ' : ( ' <L ' , 0x7C4 , None ) ,
' web_refresh ' : ( ' <H ' , 0x7CC , None ) ,
} )
Settings = [
( 0x602010E , 0xe00 , Setting_6_2_1_14 ) ,
( 0x602010A , 0xe00 , Setting_6_2_1_10 ) ,
2018-10-02 12:42:21 +01:00
( 0x6020106 , 0xe00 , Setting_6_2_1_6 ) ,
( 0x6020103 , 0xe00 , Setting_6_2_1_3 ) ,
( 0x6020102 , 0xe00 , Setting_6_2_1_2 ) ,
( 0x6020100 , 0xe00 , Setting_6_2_1 ) ,
2018-09-25 13:08:36 +01:00
( 0x6010100 , 0xe00 , Setting_6_1_1 ) ,
( 0x6000000 , 0xe00 , Setting_6_0_0 ) ,
( 0x50e0000 , 0xa00 , Setting_5_14_0 ) ,
( 0x50d0100 , 0xa00 , Setting_5_13_1 ) ,
2018-09-26 14:18:01 +01:00
( 0x50c0000 , 0x670 , Setting_5_12_0 ) ,
( 0x50b0000 , 0x670 , Setting_5_11_0 ) ,
( 0x50a0000 , 0x670 , Setting_5_10_0 ) ,
2018-09-25 13:08:36 +01:00
]
# ----------------------------------------------------------------------
# helper
# ----------------------------------------------------------------------
class Log :
INFO = ' INFO '
WARNING = ' WARNING '
ERROR = ' ERROR '
def message ( self , msg , typ = None , status = None , jsonformat = False ) :
"""
Writes a message to stdout
@param msg : string
message to output
if msg is of type dict , json format will be used
"""
if jsonformat :
message = { }
message [ ' msg ' ] = msg
if type is not None :
message [ ' type ' ] = typ
if status is not None :
message [ ' status ' ] = status
print json . dumps ( message )
else :
print ' {} {} {} {} {} ' . format ( typ if typ is not None else ' ' ,
' ' if status is not None and typ is not None else ' ' ,
status if status is not None else ' ' ,
' : ' if typ is not None else ' ' ,
msg )
2018-09-29 12:37:42 +01:00
def exit ( status = 0 , message = " end " , typ = ' ERROR ' , doexit = True ) :
2018-09-25 13:08:36 +01:00
"""
Called when the program should be exit
@param status :
the exit status program returns to callert
@param message :
the message logged before exit
2018-09-29 12:37:42 +01:00
@param typ :
message type : ' INFO ' , ' WARNING ' or ' ERROR '
@param doexit :
True to exit program , otherwise return
2018-09-25 13:08:36 +01:00
"""
logger = Log ( )
2018-09-29 12:37:42 +01:00
logger . message ( message , typ = typ if status != 0 else ' INFO ' , status = status , jsonformat = True )
exitcode = status
if doexit :
sys . exit ( exitcode )
2018-09-25 13:08:36 +01:00
# ----------------------------------------------------------------------
# Tasmota config data handling
# ----------------------------------------------------------------------
2018-10-02 12:42:21 +01:00
def GetFilenameReplaced ( filename , configuration ) :
"""
Replace variable within a filename
@param filename :
original filename possible containing replacements :
@v :
Tasmota version
@f :
FriendlyName
@return : New filename with replacements
"""
v = f1 = f2 = f3 = f4 = ' '
if ' version ' in configuration :
ver = int ( str ( configuration [ ' version ' ] ) , 0 )
major = ( ( ver >> 24 ) & 0xff )
minor = ( ( ver >> 16 ) & 0xff )
release = ( ( ver >> 8 ) & 0xff )
subrelease = ( ver & 0xff )
if major > = 6 :
if subrelease > 0 :
subreleasestr = str ( subrelease )
else :
subreleasestr = ' '
else :
if subrelease > 0 :
subreleasestr = str ( chr ( subrelease + ord ( ' a ' ) - 1 ) )
else :
subreleasestr = ' '
v = " {:d} . {:d} . {:d} {} {} " . format ( major , minor , release , ' . ' if ( major > = 6 and subreleasestr != ' ' ) else ' ' , subreleasestr )
filename = filename . replace ( ' @v ' , v )
if ' friendlyname ' in configuration :
filename = filename . replace ( ' @f ' , configuration [ ' friendlyname ' ] [ 0 ] )
return filename
2018-09-25 13:08:36 +01:00
def GetSettingsCrc ( dobj ) :
"""
Return binary config data calclulated crc
@param dobj :
2018-10-02 12:42:21 +01:00
decrypted binary config data
2018-09-25 13:08:36 +01:00
@return : 2 byte unsigned integer crc value
"""
crc = 0
for i in range ( 0 , len ( dobj ) ) :
if not i in [ 14 , 15 ] : # Skip crc
crc + = ord ( dobj [ i ] ) * ( i + 1 )
return crc & 0xffff
2018-09-29 12:37:42 +01:00
def GetFieldFormat ( fielddef ) :
2018-09-25 13:08:36 +01:00
"""
2018-09-29 12:37:42 +01:00
Return the format item of field definition
2018-09-25 13:08:36 +01:00
@param fielddef :
2018-09-29 12:37:42 +01:00
field format - see " Settings dictionary " above
2018-09-25 13:08:36 +01:00
2018-09-29 12:37:42 +01:00
@return : < format > from fielddef [ 0 ]
2018-09-25 13:08:36 +01:00
"""
2018-09-29 12:37:42 +01:00
return fielddef [ 0 ]
2018-09-25 13:08:36 +01:00
2018-09-29 12:37:42 +01:00
def GetFieldBaseAddr ( fielddef ) :
"""
Return the format item of field definition
@param fielddef :
field format - see " Settings dictionary " above
@return : < baseaddr > , < bits > , < bitshift > from fielddef [ 1 ]
"""
baseaddr = fielddef [ 1 ]
if isinstance ( baseaddr , tuple ) :
return baseaddr [ 0 ] , baseaddr [ 1 ] , baseaddr [ 2 ]
return baseaddr , 0 , 0
def MakeFieldBaseAddr ( baseaddr , bitlen , bitshift ) :
"""
Return a < baseaddr > based on given arguments
@param baseaddr :
baseaddr from Settings definition
@param bitlen :
0 or bitlen
@param bitshift :
0 or bitshift
@return : ( < baseaddr > , < bitlen > , < bitshift > ) if bitlen != 0
baseaddr if bitlen == 0
"""
if bitlen != 0 :
return ( baseaddr , bitlen , bitshift )
return baseaddr
2018-09-25 13:08:36 +01:00
2018-09-26 14:18:01 +01:00
def ConvertFieldValue ( value , fielddef , raw = False ) :
2018-09-25 13:08:36 +01:00
"""
Convert field value based on field desc
@param value :
original value read from binary data
@param fielddef
2018-09-29 12:37:42 +01:00
field definition - see " Settings dictionary " above
2018-09-26 14:18:01 +01:00
@param raw
return raw values ( True ) or converted values ( False )
2018-09-25 13:08:36 +01:00
2018-09-26 14:18:01 +01:00
@return : ( un ) converted value
2018-09-25 13:08:36 +01:00
"""
2018-09-26 14:18:01 +01:00
if not raw and len ( fielddef ) > 3 :
2018-09-29 12:37:42 +01:00
convert = fielddef [ 3 ]
if isinstance ( convert , str ) : # evaluate strings
try :
return eval ( convert . replace ( ' ? ' , ' value ' ) )
except :
return value
elif callable ( convert ) : # use as format function
return convert ( value )
2018-09-25 13:08:36 +01:00
return value
2018-09-29 12:37:42 +01:00
def GetFieldLength ( fielddef ) :
"""
Return length of a field in bytes based on field format definition
@param fielddef :
field format - see " Settings dictionary " above
@return : length of field in bytes
"""
length = 0
format_ = GetFieldFormat ( fielddef )
# get datadef from field definition
datadef = None
if len ( fielddef ) > 2 :
datadef = fielddef [ 2 ]
if datadef is not None :
# fielddef[2] contains a array or int
# calc size recursive by sum of all elements
# <datadef> contains a integer list or an single integer value
if ( isinstance ( datadef , list ) \
and len ( datadef ) > 0 \
and isinstance ( datadef [ 0 ] , int ) ) \
or isinstance ( datadef , int ) :
for i in range ( 0 , datadef [ 0 ] if isinstance ( datadef , list ) else datadef ) :
# multidimensional array
if isinstance ( datadef , list ) and len ( datadef ) > 1 :
length + = GetFieldLength ( ( fielddef [ 0 ] , fielddef [ 1 ] , fielddef [ 2 ] [ 1 : ] ) )
# single array
else :
length + = GetFieldLength ( ( fielddef [ 0 ] , fielddef [ 1 ] , None ) )
else :
if isinstance ( fielddef [ 0 ] , dict ) :
# -> iterate through format_
addr = - 1
setting = fielddef [ 0 ]
for name in setting :
baseaddr , bitlen , bitshift = GetFieldBaseAddr ( setting [ name ] )
len_ = GetFieldLength ( setting [ name ] )
if addr != baseaddr :
addr = baseaddr
length + = len_
2018-10-02 12:42:21 +01:00
2018-09-29 12:37:42 +01:00
else :
if format_ [ - 1 : ] . lower ( ) in [ ' b ' , ' c ' , ' ? ' ] :
length = 1
elif format_ [ - 1 : ] . lower ( ) in [ ' h ' ] :
length = 2
elif format_ [ - 1 : ] . lower ( ) in [ ' i ' , ' l ' , ' f ' ] :
length = 4
elif format_ [ - 1 : ] . lower ( ) in [ ' q ' , ' d ' ] :
length = 8
elif format_ [ - 1 : ] . lower ( ) in [ ' s ' , ' p ' ] :
# s and p may have a prefix as length
match = re . search ( " \ s*( \ d+) " , format_ )
if match :
length = int ( match . group ( 0 ) )
return length
def GetField ( dobj , fieldname , fielddef , raw = False , addroffset = 0 ) :
2018-09-25 13:08:36 +01:00
"""
Get field value from definition
@param dobj :
2018-10-02 12:42:21 +01:00
decrypted binary config data
2018-09-25 13:08:36 +01:00
@param fieldname :
name of the field
@param fielddef :
see Settings desc above
2018-09-26 14:18:01 +01:00
@param raw
return raw values ( True ) or converted values ( False )
2018-10-02 12:42:21 +01:00
@param addroffset
use offset for baseaddr ( used for recursive calls )
2018-09-25 13:08:36 +01:00
@return : read field value
"""
result = None
2018-09-29 12:37:42 +01:00
# get format from field definition
format_ = GetFieldFormat ( fielddef )
# get baseaddr from field definition
baseaddr , bitlen , bitshift = GetFieldBaseAddr ( fielddef )
# get datadef from field definition
datadef = None
2018-10-02 12:42:21 +01:00
if fielddef is not None and len ( fielddef ) > 2 :
2018-09-29 12:37:42 +01:00
datadef = fielddef [ 2 ]
if datadef is not None :
2018-09-25 13:08:36 +01:00
result = [ ]
2018-09-29 12:37:42 +01:00
# <datadef> contains a integer list or an single integer value
if ( isinstance ( datadef , list ) \
and len ( datadef ) > 0 \
and isinstance ( datadef [ 0 ] , int ) ) \
or isinstance ( datadef , int ) :
offset = 0
for i in range ( 0 , datadef [ 0 ] if isinstance ( datadef , list ) else datadef ) :
2018-09-25 13:08:36 +01:00
# multidimensional array
2018-09-29 12:37:42 +01:00
if isinstance ( datadef , list ) and len ( datadef ) > 1 :
if len ( fielddef ) < 4 :
subfielddef = ( fielddef [ 0 ] , MakeFieldBaseAddr ( baseaddr , bitlen , bitshift ) , datadef [ 1 : ] )
else :
subfielddef = ( fielddef [ 0 ] , MakeFieldBaseAddr ( baseaddr , bitlen , bitshift ) , datadef [ 1 : ] , fielddef [ 3 ] )
# single array
else :
if len ( fielddef ) < 4 :
subfielddef = ( fielddef [ 0 ] , MakeFieldBaseAddr ( baseaddr , bitlen , bitshift ) , None )
else :
subfielddef = ( fielddef [ 0 ] , MakeFieldBaseAddr ( baseaddr , bitlen , bitshift ) , None , fielddef [ 3 ] )
2018-09-25 13:08:36 +01:00
length = GetFieldLength ( subfielddef )
2018-10-02 12:42:21 +01:00
if length != 0 and ( fieldname != ' raw ' or args . rawkeys ) :
2018-09-29 12:37:42 +01:00
result . append ( GetField ( dobj , fieldname , subfielddef , raw = raw , addroffset = addroffset + offset ) )
offset + = length
2018-09-25 13:08:36 +01:00
else :
2018-09-29 12:37:42 +01:00
# <format> contains a dict
if isinstance ( fielddef [ 0 ] , dict ) :
# -> iterate through format_
setting = fielddef [ 0 ]
config = { }
for name in setting :
2018-10-02 12:42:21 +01:00
if name != ' raw ' or args . rawkeys :
config [ name ] = GetField ( dobj , name , setting [ name ] , raw = raw , addroffset = addroffset )
2018-09-29 12:37:42 +01:00
result = config
else :
# a simple value
if GetFieldLength ( fielddef ) != 0 :
result = struct . unpack_from ( format_ , dobj , baseaddr + addroffset ) [ 0 ]
if not format_ [ - 1 : ] . lower ( ) in [ ' s ' , ' p ' ] :
if bitshift > = 0 :
result >> = bitshift
else :
result << = abs ( bitshift )
if bitlen > 0 :
result & = ( 1 << bitlen ) - 1
# additional processing for strings
if format_ [ - 1 : ] . lower ( ) in [ ' s ' , ' p ' ] :
# use left string until \0
s = str ( result ) . split ( ' \0 ' ) [ 0 ]
# remove character > 127
result = unicode ( s , errors = ' ignore ' )
result = ConvertFieldValue ( result , fielddef , raw )
2018-09-25 13:08:36 +01:00
return result
def DeEncrypt ( obj ) :
"""
Decrpt / Encrypt binary config data
@param obj :
binary config data
@return : decrypted configuration ( if obj contains encrypted data )
encrypted configuration ( if obj contains decrypted data )
"""
dobj = obj [ 0 : 2 ]
for i in range ( 2 , len ( obj ) ) :
dobj + = chr ( ( ord ( obj [ i ] ) ^ ( CONFIG_FILE_XOR + i ) ) & 0xff )
return dobj
2018-10-02 12:42:21 +01:00
def GetTemplateSetting ( version ) :
2018-09-25 13:08:36 +01:00
"""
2018-10-02 12:42:21 +01:00
Search for template , settings and size to be used depending on given version number
2018-09-25 13:08:36 +01:00
2018-10-02 12:42:21 +01:00
@param version :
< int > version number from read binary data to search for
2018-09-25 13:08:36 +01:00
2018-10-02 12:42:21 +01:00
@return : template , settings to use , None if version is invalid
"""
2018-09-25 13:08:36 +01:00
# search setting definition
2018-09-26 14:18:01 +01:00
template = None
2018-10-02 12:42:21 +01:00
setting = None
size = None
2018-09-25 13:08:36 +01:00
for cfg in Settings :
2018-09-26 14:18:01 +01:00
if version > = cfg [ 0 ] :
2018-09-25 13:08:36 +01:00
template = cfg
2018-10-02 12:42:21 +01:00
size = template [ 1 ]
setting = template [ 2 ]
2018-09-25 13:08:36 +01:00
break
2018-10-02 12:42:21 +01:00
return template , size , setting
def Decode ( obj , raw = True ) :
"""
Decodes binary data stream
@param obj :
binary config data ( decrypted )
@param raw
decode raw values ( True ) or converted values ( False )
@return : configuration dictionary
"""
# get header data
version = GetField ( obj , ' version ' , Setting_6_2_1 [ ' version ' ] , raw = True )
template , size , setting = GetTemplateSetting ( version )
2018-09-25 13:08:36 +01:00
# if we did not found a mathching setting
2018-09-26 14:18:01 +01:00
if template is None :
2018-09-29 12:37:42 +01:00
exit ( 2 , " Tasmota configuration version 0x {:x} not supported " . format ( version ) )
2018-09-26 14:18:01 +01:00
# check size if exists
if ' cfg_size ' in setting :
cfg_size = GetField ( obj , ' cfg_size ' , setting [ ' cfg_size ' ] , raw = True )
2018-09-29 12:37:42 +01:00
# read size should be same as definied in template
2018-10-02 12:42:21 +01:00
if cfg_size > size :
2018-09-29 12:37:42 +01:00
# may be processed
exit ( 3 , " Number of bytes read does ot match - read {} , expected {} byte " . format ( cfg_size , template [ 1 ] ) , typ = ' WARNING ' , doexit = args . exitonwarning )
2018-10-02 12:42:21 +01:00
elif cfg_size < size :
2018-09-29 12:37:42 +01:00
# less number of bytes can not be processed
exit ( 3 , " Number of bytes read to small to process - read {} , expected {} byte " . format ( cfg_size , template [ 1 ] ) , typ = ' ERROR ' )
2018-09-26 14:18:01 +01:00
# check crc if exists
if ' cfg_crc ' in setting :
cfg_crc = GetField ( obj , ' cfg_crc ' , setting [ ' cfg_crc ' ] , raw = True )
else :
cfg_crc = GetSettingsCrc ( obj )
if cfg_crc != GetSettingsCrc ( obj ) :
2018-09-29 12:37:42 +01:00
exit ( 4 , ' Data CRC error, read 0x {:x} should be 0x {:x} ' . format ( cfg_crc , GetSettingsCrc ( obj ) ) , typ = ' WARNING ' , doexit = args . exitonwarning )
2018-09-25 13:08:36 +01:00
2018-10-02 12:42:21 +01:00
# get config
config = GetField ( obj , None , ( setting , None , None ) , raw = raw )
2018-09-29 12:37:42 +01:00
# add header info
timestamp = datetime . now ( )
config [ ' header ' ] = { ' timestamp ' : timestamp . strftime ( " % Y- % m- %d % H: % M: % S " ) ,
' data ' : {
' crc ' : hex ( GetSettingsCrc ( obj ) ) ,
' size ' : len ( obj ) ,
' template_version ' : hex ( template [ 0 ] ) ,
' content ' : {
' crc ' : hex ( cfg_crc ) ,
' size ' : cfg_size ,
' version ' : hex ( version ) ,
} ,
} ,
' scriptname ' : os . path . basename ( __file__ ) ,
' scriptversion ' : VER ,
}
return config
2018-09-25 13:08:36 +01:00
if __name__ == " __main__ " :
2018-09-29 12:37:42 +01:00
# program argument processing
2018-09-25 13:08:36 +01:00
parser = configargparse . ArgumentParser ( description = ' Decode configuration of Sonoff-Tasmota device. ' ,
2018-09-26 14:18:01 +01:00
epilog = ' Either argument -d <host> or -f <filename> must be given. ' )
2018-09-25 13:08:36 +01:00
source = parser . add_argument_group ( ' source ' )
source . add_argument ( ' -f ' , ' --file ' ,
metavar = ' <filename> ' ,
dest = ' tasmotafile ' ,
default = DEFAULTS [ ' source ' ] [ ' tasmotafile ' ] ,
2018-09-29 12:37:42 +01:00
help = " file to retrieve Tasmota configuration from (default: {} ) ' " . format ( DEFAULTS [ ' source ' ] [ ' tasmotafile ' ] ) )
2018-09-25 13:08:36 +01:00
source . add_argument ( ' -d ' , ' --device ' ,
2018-09-26 14:18:01 +01:00
metavar = ' <host> ' ,
2018-09-25 13:08:36 +01:00
dest = ' device ' ,
default = DEFAULTS [ ' source ' ] [ ' device ' ] ,
2018-09-29 12:37:42 +01:00
help = " hostname or IP address to retrieve Tasmota configuration from (default: {} ) " . format ( DEFAULTS [ ' source ' ] [ ' device ' ] ) )
2018-09-25 13:08:36 +01:00
source . add_argument ( ' -u ' , ' --username ' ,
metavar = ' <user> ' ,
dest = ' username ' ,
default = DEFAULTS [ ' source ' ] [ ' username ' ] ,
2018-09-29 12:37:42 +01:00
help = " host HTTP access username (default: {} ) " . format ( DEFAULTS [ ' source ' ] [ ' username ' ] ) )
2018-09-25 13:08:36 +01:00
source . add_argument ( ' -p ' , ' --password ' ,
metavar = ' <password> ' ,
dest = ' password ' ,
default = DEFAULTS [ ' source ' ] [ ' password ' ] ,
2018-09-29 12:37:42 +01:00
help = " host HTTP access password (default: {} ) " . format ( DEFAULTS [ ' source ' ] [ ' password ' ] ) )
2018-09-25 13:08:36 +01:00
2018-10-02 12:42:21 +01:00
config = parser . add_argument_group ( ' config ' )
config . add_argument ( ' --json-indent ' ,
2018-09-26 14:18:01 +01:00
metavar = ' <integer> ' ,
dest = ' jsonindent ' ,
type = int ,
2018-10-02 12:42:21 +01:00
default = DEFAULTS [ ' config ' ] [ ' jsonindent ' ] ,
help = " pretty-printed JSON output using indent level (default: ' {} ' ). Use values greater equal 0 to indent or -1 to disabled indent. " . format ( DEFAULTS [ ' config ' ] [ ' jsonindent ' ] ) )
config . add_argument ( ' --json-compact ' ,
2018-09-26 14:18:01 +01:00
dest = ' jsoncompact ' ,
action = ' store_true ' ,
2018-10-02 12:42:21 +01:00
default = DEFAULTS [ ' config ' ] [ ' jsoncompact ' ] ,
help = " compact JSON output by eliminate whitespace {} " . format ( ' (default) ' if DEFAULTS [ ' config ' ] [ ' jsoncompact ' ] else ' ' ) )
config . add_argument ( ' --sort ' ,
dest = ' sort ' ,
action = ' store_true ' ,
default = DEFAULTS [ ' config ' ] [ ' sort ' ] ,
help = " sort json keywords {} " . format ( ' (default) ' if DEFAULTS [ ' config ' ] [ ' sort ' ] else ' ' ) )
config . add_argument ( ' --unsort ' ,
dest = ' sort ' ,
action = ' store_false ' ,
default = DEFAULTS [ ' config ' ] [ ' sort ' ] ,
help = " do not sort json keywords {} " . format ( ' (default) ' if not DEFAULTS [ ' config ' ] [ ' sort ' ] else ' ' ) )
config . add_argument ( ' --raw-values ' , ' --raw ' ,
dest = ' rawvalues ' ,
2018-09-29 12:37:42 +01:00
action = ' store_true ' ,
2018-10-02 12:42:21 +01:00
default = DEFAULTS [ ' config ' ] [ ' rawvalues ' ] ,
help = " output raw values {} " . format ( ' (default) ' if DEFAULTS [ ' config ' ] [ ' rawvalues ' ] else ' ' ) )
config . add_argument ( ' --no-raw-values ' ,
dest = ' rawvalues ' ,
action = ' store_false ' ,
default = DEFAULTS [ ' config ' ] [ ' rawvalues ' ] ,
help = " output human readable values {} " . format ( ' (default) ' if not DEFAULTS [ ' config ' ] [ ' rawvalues ' ] else ' ' ) )
config . add_argument ( ' --raw-keys ' ,
dest = ' rawkeys ' ,
2018-09-25 13:08:36 +01:00
action = ' store_true ' ,
2018-10-02 12:42:21 +01:00
default = DEFAULTS [ ' config ' ] [ ' rawkeys ' ] ,
help = " output bitfield raw keys {} " . format ( ' (default) ' if DEFAULTS [ ' config ' ] [ ' rawkeys ' ] else ' ' ) )
config . add_argument ( ' --no-raw-keys ' ,
dest = ' rawkeys ' ,
action = ' store_false ' ,
default = DEFAULTS [ ' config ' ] [ ' rawkeys ' ] ,
help = " do not output bitfield raw keys {} " . format ( ' (default) ' if not DEFAULTS [ ' config ' ] [ ' rawkeys ' ] else ' ' ) )
config . add_argument ( ' --hide-pw ' ,
dest = ' hidepw ' ,
2018-09-25 13:08:36 +01:00
action = ' store_true ' ,
2018-10-02 12:42:21 +01:00
default = DEFAULTS [ ' config ' ] [ ' hidepw ' ] ,
help = " hide passwords {} " . format ( ' (default) ' if DEFAULTS [ ' config ' ] [ ' hidepw ' ] else ' ' ) )
config . add_argument ( ' --unhide-pw ' ,
dest = ' hidepw ' ,
action = ' store_false ' ,
default = DEFAULTS [ ' config ' ] [ ' hidepw ' ] ,
help = " unhide passwords {} " . format ( ' (default) ' if not DEFAULTS [ ' config ' ] [ ' hidepw ' ] else ' ' ) )
config . add_argument ( ' -o ' , ' --output-file ' ,
2018-09-25 13:08:36 +01:00
metavar = ' <filename> ' ,
dest = ' outputfile ' ,
2018-10-02 12:42:21 +01:00
default = DEFAULTS [ ' config ' ] [ ' outputfile ' ] ,
help = " file to store configuration to (default: {} ). Replacements: @v=Tasmota version, @f=friendly name " . format ( DEFAULTS [ ' config ' ] [ ' outputfile ' ] ) )
config . add_argument ( ' --output-file-format ' ,
2018-09-29 12:37:42 +01:00
metavar = ' <word> ' ,
dest = ' outputfileformat ' ,
choices = [ ' json ' , ' binary ' ] ,
2018-10-02 12:42:21 +01:00
default = DEFAULTS [ ' config ' ] [ ' outputfileformat ' ] ,
help = " output format ( ' json ' or ' binary ' , default: ' {} ' ) " . format ( DEFAULTS [ ' config ' ] [ ' outputfileformat ' ] ) )
2018-09-25 13:08:36 +01:00
parser . add_argument ( ' -c ' , ' --config ' ,
metavar = ' <filename> ' ,
dest = ' configfile ' ,
default = DEFAULTS [ ' DEFAULT ' ] [ ' configfile ' ] ,
is_config_file = True ,
2018-09-29 12:37:42 +01:00
help = " Config file, can be used instead of command parameter (default: {} ) " . format ( DEFAULTS [ ' DEFAULT ' ] [ ' configfile ' ] ) )
parser . add_argument ( ' --exit-on-error-only ' ,
dest = ' exitonwarning ' ,
action = ' store_false ' ,
default = DEFAULTS [ ' DEFAULT ' ] [ ' exitonwarning ' ] ,
help = " exit on error only (default: {} ). Not recommended, used by your own responsibility! " . format ( ' exit on ERROR and WARNING ' if DEFAULTS [ ' DEFAULT ' ] [ ' exitonwarning ' ] else ' exit on ERROR ' ) )
2018-09-25 13:08:36 +01:00
info = parser . add_argument_group ( ' info ' )
info . add_argument ( ' -V ' , ' --version ' , action = ' version ' , version = PROG )
args = parser . parse_args ( )
2018-10-02 12:42:21 +01:00
2018-09-29 12:37:42 +01:00
# default no configuration available
2018-09-25 13:08:36 +01:00
configobj = None
2018-10-02 12:42:21 +01:00
2018-09-29 12:37:42 +01:00
# check source args
if args . device is not None and args . tasmotafile is not None :
exit ( 6 , " Only one source allowed. Do not use -d and -f together " )
2018-10-02 12:42:21 +01:00
2018-09-29 12:37:42 +01:00
# read config direct from device via http
2018-09-25 13:08:36 +01:00
if args . device is not None :
buffer = io . BytesIO ( )
url = str ( " http:// {} /dl " . format ( args . device ) )
c = pycurl . Curl ( )
c . setopt ( c . URL , url )
c . setopt ( c . VERBOSE , 0 )
if args . username is not None and args . password is not None :
c . setopt ( c . HTTPAUTH , c . HTTPAUTH_BASIC )
c . setopt ( c . USERPWD , args . username + ' : ' + args . password )
c . setopt ( c . WRITEDATA , buffer )
try :
c . perform ( )
except Exception , e :
exit ( e [ 0 ] , e [ 1 ] )
response = c . getinfo ( c . RESPONSE_CODE )
c . close ( )
if response > = 400 :
exit ( response , ' HTTP returns {} ' . format ( response ) )
configobj = buffer . getvalue ( )
2018-09-29 12:37:42 +01:00
# read config from a file
2018-09-25 13:08:36 +01:00
elif args . tasmotafile is not None :
if not os . path . isfile ( args . tasmotafile ) : # check file exists
2018-09-29 12:37:42 +01:00
exit ( 1 , " File ' {} ' not found " . format ( args . tasmotafile ) )
2018-09-25 13:08:36 +01:00
try :
tasmotafile = open ( args . tasmotafile , " rb " )
configobj = tasmotafile . read ( )
tasmotafile . close ( )
except Exception , e :
exit ( e [ 0 ] , e [ 1 ] )
2018-09-29 12:37:42 +01:00
# no config source given
2018-09-25 13:08:36 +01:00
else :
parser . print_help ( )
sys . exit ( 0 )
if configobj is not None and len ( configobj ) > 0 :
cfg = DeEncrypt ( configobj )
2018-10-02 12:42:21 +01:00
configuration = Decode ( cfg , args . rawvalues )
2018-09-25 13:08:36 +01:00
2018-09-29 12:37:42 +01:00
# output to file
if args . outputfile is not None :
2018-10-02 12:42:21 +01:00
outputfilename = GetFilenameReplaced ( args . outputfile , configuration )
2018-09-29 12:37:42 +01:00
if args . outputfileformat == ' binary ' :
outputfile = open ( outputfilename , " wb " )
outputfile . write ( struct . pack ( ' <L ' , BINARYFILE_MAGIC ) )
outputfile . write ( cfg )
outputfile . close ( )
2018-10-02 12:42:21 +01:00
2018-09-29 12:37:42 +01:00
if args . outputfileformat == ' json ' :
2018-10-02 12:42:21 +01:00
configuration = Decode ( cfg , raw = True )
2018-09-29 12:37:42 +01:00
outputfile = open ( outputfilename , " w " )
2018-10-02 12:42:21 +01:00
json . dump ( configuration , outputfile , sort_keys = args . sort , indent = None if args . jsonindent < 0 else args . jsonindent , separators = ( ' , ' , ' : ' ) if args . jsoncompact else ( ' , ' , ' : ' ) )
2018-09-29 12:37:42 +01:00
outputfile . close ( )
# output to screen
2018-10-02 12:42:21 +01:00
else :
print json . dumps ( configuration , sort_keys = args . sort , indent = None if args . jsonindent < 0 else args . jsonindent , separators = ( ' , ' , ' : ' ) if args . jsoncompact else ( ' , ' , ' : ' ) )
2018-09-25 13:08:36 +01:00
else :
2018-09-29 12:37:42 +01:00
exit ( 5 , " Unable to read configuration data from {} ' {} ' " . format ( ' device ' if args . device is not None else ' file ' , \
2018-09-26 14:18:01 +01:00
args . device if args . device is not None else args . tasmotafile ) )
2018-09-29 12:37:42 +01:00
sys . exit ( exitcode )