From 8f95f07b518010a6c3bf37a2ac34a02b5309fd5e Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 28 Sep 2018 17:26:08 +0200 Subject: [PATCH 01/18] Add RF to MagicHome / LC10 Add RF Receiver control to module MagicHome to be used on Arilux LC10 (#3792) --- sonoff/_changelog.ino | 1 + sonoff/sonoff_template.h | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index abf4572f6..efd0944b7 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,5 +1,6 @@ /* 6.2.1.9 20180928 * Add Apparent Power and Reactive Power to Energy Monitoring devices (#251) + * Add RF Receiver control to module MagicHome to be used on Arilux LC10 (#3792) * * 6.2.1.8 20180926 * Change status JSON message providing more switch and retain information diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 8374fa09d..d083675d3 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -855,6 +855,7 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0, 0, 0, 0, 0, // Flash connection 0, 0, 0, 0, 0 }, +/* { "MagicHome", // Magic Home (aka Flux-light) (ESP8266) // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html 0, @@ -869,6 +870,22 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_PWM1, // GPIO14 RGB LED Red 0, 0, 0 }, +*/ + { "MagicHome", // Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285) + // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html + 0, + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_LED1_INV, // GPIO02 Blue onboard LED + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) + GPIO_PWM2, // GPIO05 RGB LED Green + 0, 0, 0, 0, 0, 0, // Flash connection + GPIO_PWM3, // GPIO12 RGB LED Blue + GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White as used on Arilux LC10) + GPIO_PWM1, // GPIO14 RGB LED Red + GPIO_LED2_INV, // GPIO15 RF receiver control + 0, 0 + }, { "Luani HVIO", // ESP8266_HVIO // https://luani.de/projekte/esp8266-hvio/ 0, // GPIO00 Flash jumper From aa0407493731ed841d9dfe3819247d9052dbb12c Mon Sep 17 00:00:00 2001 From: znanev <20048364+znanev@users.noreply.github.com> Date: Fri, 28 Sep 2018 23:21:17 +0100 Subject: [PATCH 02/18] Fix dead links to wiki pages --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 498cd2f80..524ad49ab 100644 --- a/README.md +++ b/README.md @@ -93,8 +93,8 @@ The following devices are supported: - [BlitzWolf BW-SHP2 Smart Socket with Energy Monitoring](https://www.banggood.com/BlitzWolf-BW-SHP2-Smart-WIFI-Socket-EU-Plug-220V-16A-Work-with-Amazon-Alexa-Google-Assistant-p-1292899.html) - [Luani HVIO board](https://luani.de/projekte/esp8266-hvio/) - [Wemos D1 mini](https://wiki.wemos.cc/products:d1:d1_mini) -- [HuaFan Smart Socket](HuaFan-Smart-Socket) -- [Hyleton-313 Smart Plug](Hyleton-313-Smart-Plug) +- [HuaFan Smart Socket](https://github.com/arendst/Sonoff-Tasmota/wiki/HuaFan-Smart-Socket) +- [Hyleton-313 Smart Plug](https://github.com/arendst/Sonoff-Tasmota/wiki/Hyleton-313-Smart-Plug) - [Allterco Shelly 1](https://shelly.cloud/shelly1-open-source/) - [Allterco Shelly 2 with Energy Monitoring](https://shelly.cloud/shelly2/) - NodeMcu and Ledunia From 8255c001b6ac687a6b5007846b194ca708c3fa23 Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 29 Sep 2018 10:12:32 +0200 Subject: [PATCH 03/18] Use color2 command (HASS sends normalized RGB) --- sonoff/xdrv_12_home_assistant.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/xdrv_12_home_assistant.ino b/sonoff/xdrv_12_home_assistant.ino index b085947ae..304d5fc2f 100644 --- a/sonoff/xdrv_12_home_assistant.ino +++ b/sonoff/xdrv_12_home_assistant.ino @@ -50,7 +50,7 @@ const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM = "\"brightness_value_template\":\"{{value_json." D_CMND_DIMMER "}}\""; const char HASS_DISCOVER_LIGHT_COLOR[] PROGMEM = - "%s,\"rgb_command_topic\":\"%s\"," // cmnd/led2/Color + "%s,\"rgb_command_topic\":\"%s2\"," // cmnd/led2/Color2 "\"rgb_state_topic\":\"%s\"," // stat/led2/RESULT "\"rgb_value_template\":\"{{value_json." D_CMND_COLOR "}}\""; // "\"rgb_value_template\":\"{{value_json." D_CMND_COLOR " | join(',')}}\""; From 2252be521ab58afed0bd65f0247ed2f3c445b74b Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 29 Sep 2018 12:34:24 +0200 Subject: [PATCH 04/18] Fix I2CScan invalid JSON Fix I2CScan invalid JSON error message (#3925) --- sonoff/_changelog.ino | 1 + sonoff/sonoff_template.h | 34 ++++++++++++++++------------------ sonoff/support.ino | 24 ++++++++++++++++-------- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index efd0944b7..2cc23ac4a 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,6 +1,7 @@ /* 6.2.1.9 20180928 * Add Apparent Power and Reactive Power to Energy Monitoring devices (#251) * Add RF Receiver control to module MagicHome to be used on Arilux LC10 (#3792) + * Fix I2CScan invalid JSON error message (#3925) * * 6.2.1.8 20180926 * Change status JSON message providing more switch and retain information diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index d083675d3..55b1ce3aa 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -855,35 +855,19 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0, 0, 0, 0, 0, // Flash connection 0, 0, 0, 0, 0 }, -/* - { "MagicHome", // Magic Home (aka Flux-light) (ESP8266) - // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html - 0, - GPIO_USER, // GPIO01 Serial RXD and Optional sensor - GPIO_LED1_INV, // GPIO02 Blue onboard LED - GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_USER, // GPIO04 IR receiver (optional) - GPIO_PWM2, // GPIO05 RGB LED Green - 0, 0, 0, 0, 0, 0, // Flash connection - GPIO_PWM3, // GPIO12 RGB LED Blue - GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White) - GPIO_PWM1, // GPIO14 RGB LED Red - 0, 0, 0 - }, -*/ { "MagicHome", // Magic Home (aka Flux-light) (ESP8266) and Arilux LC10 (ESP8285) // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html 0, GPIO_USER, // GPIO01 Serial RXD and Optional sensor GPIO_LED1_INV, // GPIO02 Blue onboard LED GPIO_USER, // GPIO03 Serial TXD and Optional sensor - GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) + GPIO_ARIRFRCV, // GPIO04 IR or RF receiver (optional) (Arilux LC10) GPIO_PWM2, // GPIO05 RGB LED Green 0, 0, 0, 0, 0, 0, // Flash connection GPIO_PWM3, // GPIO12 RGB LED Blue GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White as used on Arilux LC10) GPIO_PWM1, // GPIO14 RGB LED Red - GPIO_LED2_INV, // GPIO15 RF receiver control + GPIO_LED2_INV, // GPIO15 RF receiver control (Arilux LC10) 0, 0 }, { "Luani HVIO", // ESP8266_HVIO @@ -1089,6 +1073,20 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { /* Optionals + { "MagicHome", // Magic Home (aka Flux-light) (ESP8266) + // https://www.aliexpress.com/item/Magic-Home-Mini-RGB-RGBW-Wifi-Controller-For-Led-Strip-Panel-light-Timing-Function-16million-colors/32686853650.html + 0, + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_LED1_INV, // GPIO02 Blue onboard LED + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_USER, // GPIO04 IR receiver (optional) + GPIO_PWM2, // GPIO05 RGB LED Green + 0, 0, 0, 0, 0, 0, // Flash connection + GPIO_PWM3, // GPIO12 RGB LED Blue + GPIO_USER, // GPIO13 RGBW LED White (optional - set to PWM4 for Cold White or Warm White) + GPIO_PWM1, // GPIO14 RGB LED Red + 0, 0, 0 + }, { "Arilux LC10", // Arilux LC10 (ESP8285), RGBW + RF // https://github.com/arendst/Sonoff-Tasmota/wiki/MagicHome-with-ESP8285 // https://www.aliexpress.com/item/DC5-24V-Wireless-WIFI-LED-RGB-Controller-RGBW-Controller-IR-RF-Remote-Control-IOS-Android-for/32827253255.html diff --git a/sonoff/support.ino b/sonoff/support.ino index fc73e726e..3665650dd 100644 --- a/sonoff/support.ino +++ b/sonoff/support.ino @@ -1775,27 +1775,35 @@ int8_t I2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len void I2cScan(char *devs, unsigned int devs_len) { - byte error; - byte address; + // Return error codes defined in twi.h and core_esp8266_si2c.c + // I2C_OK 0 + // I2C_SCL_HELD_LOW 1 = SCL held low by another device, no procedure available to recover + // I2C_SCL_HELD_LOW_AFTER_READ 2 = I2C bus error. SCL held low beyond slave clock stretch time + // I2C_SDA_HELD_LOW 3 = I2C bus error. SDA line held low by slave/another_master after n bits + // I2C_SDA_HELD_LOW_AFTER_INIT 4 = line busy. SDA again held low by another device. 2nd master? + + byte error = 0; + byte address = 0; byte any = 0; - char tstr[10]; snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"" D_JSON_I2CSCAN_DEVICES_FOUND_AT)); for (address = 1; address <= 127; address++) { Wire.beginTransmission(address); error = Wire.endTransmission(); if (0 == error) { - snprintf_P(tstr, sizeof(tstr), PSTR(" 0x%2x"), address); - strncat(devs, tstr, devs_len); any = 1; + snprintf_P(devs, devs_len, PSTR("%s 0x%02x"), devs, address); } - else if (4 == error) { - snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"" D_JSON_I2CSCAN_UNKNOWN_ERROR_AT " 0x%2x\"}"), address); + else if (error != 2) { // Seems to happen anyway using this scan + any = 2; + snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"Error %d at 0x%02x"), error, address); + break; } } if (any) { strncat(devs, "\"}", devs_len); - } else { + } + else { snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"" D_JSON_I2CSCAN_NO_DEVICES_FOUND "\"}")); } } From 3b3579f1db1c5e06fafa90050e2a3ef63f0b53e5 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 29 Sep 2018 13:09:51 +0200 Subject: [PATCH 05/18] Fix config crc errors Fix invalid configuration restores and decode_config.py crc error when savedata = 0 (#3918) --- sonoff/_changelog.ino | 1 + sonoff/xdrv_02_webserver.ino | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 2cc23ac4a..0a113a04f 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -2,6 +2,7 @@ * Add Apparent Power and Reactive Power to Energy Monitoring devices (#251) * Add RF Receiver control to module MagicHome to be used on Arilux LC10 (#3792) * Fix I2CScan invalid JSON error message (#3925) + * Fix invalid configuration restores and decode_config.py crc error when savedata = 0 (#3918) * * 6.2.1.8 20180926 * Change status JSON message providing more switch and retain information diff --git a/sonoff/xdrv_02_webserver.ino b/sonoff/xdrv_02_webserver.ino index 5182b7051..6a939dc24 100644 --- a/sonoff/xdrv_02_webserver.ino +++ b/sonoff/xdrv_02_webserver.ino @@ -1013,6 +1013,10 @@ void HandleBackupConfiguration() WebServer->sendHeader(F("Content-Disposition"), attachment); WebServer->send(200, FPSTR(HDR_CTYPE_STREAM), ""); + + uint16_t cfg_crc = Settings.cfg_crc; + Settings.cfg_crc = GetSettingsCrc(); // Calculate crc (again) as it might be wrong when savedata = 0 (#3918) + memcpy(settings_buffer, &Settings, sizeof(Settings)); if (config_xor_on_set) { for (uint16_t i = 2; i < sizeof(Settings); i++) { @@ -1030,6 +1034,8 @@ void HandleBackupConfiguration() #endif SettingsBufferFree(); + + Settings.cfg_crc = cfg_crc; // Restore crc in case savedata = 0 to make sure settings will be noted as changed } void HandleSaveSettings() From 3997792429d92b24d441911952a93bc51fe51e9c Mon Sep 17 00:00:00 2001 From: Norbert Richter Date: Sat, 29 Sep 2018 13:37:42 +0200 Subject: [PATCH 06/18] v1.5.0011: 'decode-config.py' fixes & enhancements - add configuration data header to json and binary outputs --add template @v and @f for output filename - add output of crc values on crc error - add '--exit-on-error-only' arg make it possible to process partly valid data - replaced '--sort ' by '--unsort' - changed data size and data crc error into warnings - changed output always in json format - removed arg '--format' (obsolete) - removed collections module (obsolete) v1.5.0010: 'decode-config.py' add more detailed outputs - add bit structure for struct TimeRule, Timer, Mcp230xxCfg - add program return code desc - add error handling for additonal python modules - changed 'Settings' definition to handle array of struct - changed field conversion string to formular evaluation for values - fix and enhance output values --- tools/decode-config.py | 1631 ++++++++++++++++++++++++---------------- 1 file changed, 983 insertions(+), 648 deletions(-) diff --git a/tools/decode-config.py b/tools/decode-config.py index dc51e0f42..284904fb9 100644 --- a/tools/decode-config.py +++ b/tools/decode-config.py @@ -19,23 +19,25 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . + Requirements: - Python - pip install json pycurl urllib2 configargparse + Instructions: - Execute command with option -d to retrieve config data from device or - use -f to read out a previously saved configuration file. + 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 For help execute command with argument -h Usage: decode-config.py [-h] [-f ] [-d ] [-u ] - [-p ] [--format ] - [--json-indent ] [--json-compact] - [--sort ] [--raw] [--unhide-pw] [-o ] - [-c ] [-V] + [-p ] [--json-indent ] + [--json-compact] [--unsort] [--raw] [--unhide-pw] + [-o ] [--output-file-format ] + [-c ] [--exit-on-error-only] [-V] 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 @@ -48,89 +50,84 @@ Usage: -c , --config Config file, can be used instead of command parameter (default: None) + --exit-on-error-only exit on error only (default: exit on ERROR and + WARNING). Not recommended, used by your own + responsibility! source: -f , --file file to retrieve Tasmota configuration from (default: - None) + None)' -d , --device hostname or IP address to retrieve Tasmota configuration from (default: None) -u , --username - host http access username (default: admin) + host HTTP access username (default: admin) -p , --password - host http access password (default: None) + host HTTP access password (default: None) output: - --format output format ("json" or "text", default: "json") --json-indent pretty-printed JSON output using indent level - (default: "None") + (default: 'None') --json-compact compact JSON output by eliminate whitespace (default: - "not compact") - --sort sort result - can be "none" or "name" (default: - "name") - --raw output raw values (default: processed) + normal) + --unsort do not sort results (default: sort) + --raw output raw values (default: process) --unhide-pw unhide passwords (default: hide) -o , --output-file - file to store decrypted raw binary configuration to - (default: None) + file to store configuration to (default: None) Macros: + @v=Tasmota version, @f=friendly name + --output-file-format + output format ('json' or 'binary', default: 'json') info: -V, --version show program's version number and exit Either argument -d or -f must be given. - -Examples: - Read configuration from hostname 'sonoff1' and output default json config - ./decode-config.py -d sonoff1 - - Read configuration from file 'Config__6.2.1.dmp' and output default json config - ./decode-config.py -f Config__6.2.1.dmp - - Read configuration from hostname 'sonoff1' using web login data - ./decode-config.py -d sonoff1 -u admin -p xxxx - - Read configuration from hostname 'sonoff1' using web login data and unhide passwords - ./decode-config.py -d sonoff1 -u admin -p xxxx --unhide-pw - - Read configuration from hostname 'sonoff1' using web login data, unhide passwords - and sort key names - ./decode-config.py -d sonoff1 -u admin -p xxxx --unhide-pw --sort name """ import os.path import io import sys -import configargparse -import collections import struct import re -import json +import math +from datetime import datetime +try: + import json +except ImportError: + print("module not found. Try 'pip install json' to install it") + sys.exit(9) +try: + import configargparse +except ImportError: + print("module not found. Try 'pip install configargparse' to install it") + sys.exit(9) try: import pycurl except ImportError: - print("module not found. Try 'pip pycurl' to install it") + print("module not found. Try 'pip install pycurl' to install it") sys.exit(9) try: import urllib2 except ImportError: - print("module not found. Try 'pip urllib2' to install it") + print("module not found. Try 'pip install urllib2' to install it") sys.exit(9) -VER = '1.5.0009' +VER = '1.5.0011' PROG='{} v{} by Norbert Richter'.format(os.path.basename(sys.argv[0]),VER) CONFIG_FILE_XOR = 0x5A - args = {} DEFAULTS = { 'DEFAULT': { 'configfile': None, + 'exitonwarning':True, }, 'source': { @@ -141,15 +138,16 @@ DEFAULTS = { }, 'output': { - 'format': 'json', 'jsonindent': None, 'jsoncompact': False, - 'sort': 'name', + 'unsort': False, 'raw': False, 'unhide-pw': False, 'outputfile': None, + 'outputfileformat': 'json', }, } +exitcode = 0 """ @@ -163,11 +161,35 @@ Settings dictionary describes the config file fields definition: format Define the data interpretation. - For details see struct module format string - https://docs.python.org/2.7/library/struct.html#format-strings + 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 + 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 + The address (starting from 0) within config data. + For bit fields must be a tuple. + n: + Defines a simple address within config data. + must be a positive integer. + (n, b, s): + A tuple defines a bit field: + + is the address within config data (integer) + + how many bits are used (positive integer) + + bit shift (integer) + positive shift the result right bits + negative shift the result left bits datadef Define the field interpretation different from simple @@ -182,91 +204,74 @@ Settings dictionary describes the config file fields definition: Defines a one-dimensional array of size [n, n <,n...>] Defines a multi-dimensional array - [{} <,{}...] - Defines a bit struct. The items are simply dict - {'bitname', bitlen}, the dict order is important. convert (optional) Define an output/conversion methode, can be a simple string or a previously defined function name. - 'xxx': - a string defines a format specification of the string - formatter, see - https://docs.python.org/2.7/library/string.html#format-string-syntax + '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 baudrate(value): - return value * 1200 - def int2ip(value): return '{:d}.{:d}.{:d}.{:d}'.format(value & 0xff, value>>8 & 0xff, value>>16 & 0xff, value>>24 & 0xff) -def int2geo(value): - return float(value) / 1000000 - def password(value): if args.unhidepw: return value return '********' -def fingerprintstr(value): - s = list(value) - result = '' - for c in s: - if c in '0123456789abcdefABCDEF': - result += c - return result - - Setting_6_2_1 = { 'cfg_holder': (' from fielddef[0] """ + return fielddef[0] - length=0 - if fielddef[2] is not None: - # fielddef[2] contains a array or int - # calc size recursive by sum of all elements +def GetFieldBaseAddr(fielddef): + """ + Return the format item of field definition - # tuple 2 contains a list with integer or an integer value - if (isinstance(fielddef[2], list) and len(fielddef[2])>0 and isinstance(fielddef[2][0], int)) or isinstance(fielddef[2], int): - for i in range(0, fielddef[2][0] if isinstance(fielddef[2], list) else fielddef[2] ): - # multidimensional array - if isinstance(fielddef[2], list) and len(fielddef[2])>1: - length += GetFieldLength( (fielddef[0], fielddef[1], fielddef[2][1:]) ) - else: - length += GetFieldLength( (fielddef[0], fielddef[1], None) ) - else: - if fielddef[0][-1:].lower() in ['b','c','?']: - length=1 - elif fielddef[0][-1:].lower() in ['h']: - length=2 - elif fielddef[0][-1:].lower() in ['i','l','f']: - length=4 - elif fielddef[0][-1:].lower() in ['q','d']: - length=8 - elif fielddef[0][-1:].lower() in ['s','p']: - # s and p needs prefix as length - match = re.search("\s*(\d+)", fielddef[0]) - if match: - length=int(match.group(0)) + @param fielddef: + field format - see "Settings dictionary" above + + @return: ,, 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 based on given arguments + + @param baseaddr: + baseaddr from Settings definition + @param bitlen: + 0 or bitlen + @param bitshift: + 0 or bitshift + + @return: (,,) if bitlen != 0 + baseaddr if bitlen == 0 + + """ + if bitlen!=0: + return (baseaddr, bitlen, bitshift) + return baseaddr - # it's a single value - return length def ConvertFieldValue(value, fielddef, raw=False): """ @@ -1682,21 +1850,94 @@ def ConvertFieldValue(value, fielddef, raw=False): @param value: original value read from binary data @param fielddef - field definition (contains possible conversion defiinition) + field definition - see "Settings dictionary" above @param raw return raw values (True) or converted values (False) @return: (un)converted value """ if not raw and len(fielddef)>3: - if isinstance(fielddef[3],str): # use a format string - return fielddef[3].format(value) - elif callable(fielddef[3]): # use a format function - return fielddef[3](value) + 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) return value -def GetField(dobj, fieldname, fielddef, raw=False): +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 + + # 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_ + + 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): """ Get field value from definition @@ -1714,45 +1955,78 @@ def GetField(dobj, fieldname, fielddef, raw=False): result = None - if fielddef[2] is not None: + # 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 + if len(fielddef)>2: + datadef = fielddef[2] + + if datadef is not None: result = [] - # tuple 2 contains a list with integer or an integer value - if (isinstance(fielddef[2], list) and len(fielddef[2])>0 and isinstance(fielddef[2][0], int)) or isinstance(fielddef[2], int): - addr = fielddef[1] - for i in range(0, fielddef[2][0] if isinstance(fielddef[2], list) else fielddef[2] ): + # 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): + # multidimensional array - if isinstance(fielddef[2], list) and len(fielddef[2])>1: - subfielddef = (fielddef[0], addr, fielddef[2][1:], None if len(fielddef)<4 else fielddef[3]) - else: # single array - subfielddef = (fielddef[0], addr, None, None if len(fielddef)<4 else fielddef[3]) + 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]) + length = GetFieldLength(subfielddef) if length != 0: - result.append(GetField(dobj, fieldname, subfielddef, raw)) - addr += length - # tuple 2 contains a list with dict - elif isinstance(fielddef[2], list) and len(fielddef[2])>0 and isinstance(fielddef[2][0], dict): - d = {} - value = struct.unpack_from(fielddef[0], dobj, fielddef[1])[0] - d['base'] = ConvertFieldValue(value, fielddef, raw); - union = fielddef[2] - i = 0 - for l in union: - for name,bits in l.items(): - bitval = (value & ( ((1<> i - d[name] = bitval - i += bits - result = d + result.append(GetField(dobj, fieldname, subfielddef, raw=raw, addroffset=addroffset+offset)) + offset += length + else: - # it's a single value - if GetFieldLength(fielddef) != 0: - result = struct.unpack_from(fielddef[0], dobj, fielddef[1])[0] - if fielddef[0][-1:].lower() in ['s','p']: - if ord(result[:1])==0x00 or ord(result[:1])==0xff: - result = '' - s = str(result).split('\0')[0] - result = unicode(s, errors='replace') - result = ConvertFieldValue(result, fielddef, raw) + # contains a dict + if isinstance(fielddef[0], dict): + # -> iterate through format_ + setting = fielddef[0] + config = {} + for name in setting: + config[name] = GetField(dobj, name, setting[name], raw=args.raw, addroffset=addroffset) + 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< 127 + result = unicode(s, errors='ignore') + + result = ConvertFieldValue(result, fielddef, raw) return result @@ -1779,6 +2053,8 @@ def Decode(obj): @param obj: binary config data (decrypted) + + @return: configuration dictionary """ # get header data version = GetField(obj, 'version', Setting_6_2_1['version'], raw=True) @@ -1792,16 +2068,20 @@ def Decode(obj): # if we did not found a mathching setting if template is None: - exit(2, "Can't handle Tasmota configuration data for version 0x{:x}".format(version) ) - + exit(2, "Tasmota configuration version 0x{:x} not supported".format(version) ) + setting = template[2] # check size if exists if 'cfg_size' in setting: cfg_size = GetField(obj, 'cfg_size', setting['cfg_size'], raw=True) - # if we did not found a mathching setting - if cfg_size != template[1]: - exit(2, "Data size does not match. Expected {} bytes, read {} bytes.".format(template[1], cfg_size) ) + # read size should be same as definied in template + if cfg_size > template[1]: + # 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) + elif cfg_size < template[1]: + # 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') # check crc if exists if 'cfg_crc' in setting: @@ -1809,25 +2089,34 @@ def Decode(obj): else: cfg_crc = GetSettingsCrc(obj) if cfg_crc != GetSettingsCrc(obj): - exit(3, 'Data crc error' ) + exit(4, 'Data CRC error, read 0x{:x} should be 0x{:x}'.format(cfg_crc, GetSettingsCrc(obj)), typ='WARNING', doexit=args.exitonwarning) config = {} - config['version_template'] = '0x{:x}'.format(template[0]) for name in setting: - config[name] = GetField(obj, name, setting[name], args.raw) + config[name] = GetField(obj, name, setting[name], raw=args.raw) - if args.sort == 'name': - config = collections.OrderedDict(sorted(config.items())) - - if args.format == 'json': - print json.dumps(config, sort_keys=args.sort=='name', indent=args.jsonindent, separators=(',', ':') if args.jsoncompact else (', ', ': ') ) - else: - for key,value in config.items(): - print '{} = {}'.format(key, repr(value)) + # 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 if __name__ == "__main__": + # program argument processing parser = configargparse.ArgumentParser(description='Decode configuration of Sonoff-Tasmota device.', epilog='Either argument -d or -f must be given.') @@ -1836,80 +2125,89 @@ if __name__ == "__main__": metavar='', dest='tasmotafile', default=DEFAULTS['source']['tasmotafile'], - help='file to retrieve Tasmota configuration from (default: {})'.format(DEFAULTS['source']['tasmotafile'])) + help="file to retrieve Tasmota configuration from (default: {})'".format(DEFAULTS['source']['tasmotafile'])) source.add_argument('-d', '--device', metavar='', dest='device', default=DEFAULTS['source']['device'], - help='hostname or IP address to retrieve Tasmota configuration from (default: {})'.format(DEFAULTS['source']['device']) ) + help="hostname or IP address to retrieve Tasmota configuration from (default: {})".format(DEFAULTS['source']['device']) ) source.add_argument('-u', '--username', metavar='', dest='username', default=DEFAULTS['source']['username'], - help='host http access username (default: {})'.format(DEFAULTS['source']['username'])) + help="host HTTP access username (default: {})".format(DEFAULTS['source']['username'])) source.add_argument('-p', '--password', metavar='', dest='password', default=DEFAULTS['source']['password'], - help='host http access password (default: {})'.format(DEFAULTS['source']['password'])) + help="host HTTP access password (default: {})".format(DEFAULTS['source']['password'])) output = parser.add_argument_group('output') - output.add_argument('--format', - metavar='', - dest='format', - choices=['json', 'text'], - default=DEFAULTS['output']['format'], - help='output format ("json" or "text", default: "{}")'.format(DEFAULTS['output']['format']) ) output.add_argument('--json-indent', metavar='', dest='jsonindent', type=int, default=DEFAULTS['output']['jsonindent'], - help='pretty-printed JSON output using indent level (default: "{}")'.format(DEFAULTS['output']['jsonindent']) ) + help="pretty-printed JSON output using indent level (default: '{}')".format(DEFAULTS['output']['jsonindent']) ) output.add_argument('--json-compact', dest='jsoncompact', action='store_true', default=DEFAULTS['output']['jsoncompact'], - help='compact JSON output by eliminate whitespace (default: "{}")'.format('compact' if DEFAULTS['output']['jsoncompact'] else 'not compact') ) - output.add_argument('--sort', - metavar='', - dest='sort', - choices=['none', 'name'], - default=DEFAULTS['output']['sort'], - help='sort result - can be "none" or "name" (default: "{}")'.format(DEFAULTS['output']['sort']) ) + help="compact JSON output by eliminate whitespace (default: {})".format('normal' if not DEFAULTS['output']['jsoncompact'] else 'compact') ) + output.add_argument('--unsort', + dest='unsort', + action='store_true', + default=DEFAULTS['output']['unsort'], + help="do not sort results (default: {})".format('sort' if not DEFAULTS['output']['unsort'] else 'unsort') ) output.add_argument('--raw', dest='raw', action='store_true', default=DEFAULTS['output']['raw'], - help='output raw values (default: {})'.format('raw' if DEFAULTS['output']['raw'] else 'processed') ) + help="output raw values (default: {})".format('raw' if DEFAULTS['output']['raw'] else 'process') ) output.add_argument('--unhide-pw', dest='unhidepw', action='store_true', default=DEFAULTS['output']['unhide-pw'], - help='unhide passwords (default: {})'.format('unhide' if DEFAULTS['output']['unhide-pw'] else 'hide') ) + help="unhide passwords (default: {})".format('unhide' if DEFAULTS['output']['unhide-pw'] else 'hide') ) output.add_argument('-o', '--output-file', metavar='', dest='outputfile', default=DEFAULTS['output']['outputfile'], - help='file to store decrypted raw binary configuration to (default: {})'.format(DEFAULTS['output']['outputfile'])) + help="file to store configuration to (default: {}) Macros: @v=Tasmota version, @f=friendly name".format(DEFAULTS['output']['outputfile'])) + output.add_argument('--output-file-format', + metavar='', + dest='outputfileformat', + choices=['json', 'binary'], + default=DEFAULTS['output']['outputfileformat'], + help="output format ('json' or 'binary', default: '{}')".format(DEFAULTS['output']['outputfileformat']) ) parser.add_argument('-c', '--config', metavar='', dest='configfile', default=DEFAULTS['DEFAULT']['configfile'], is_config_file=True, - help='Config file, can be used instead of command parameter (default: {})'.format(DEFAULTS['DEFAULT']['configfile']) ) + 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') ) info = parser.add_argument_group('info') info.add_argument('-V', '--version', action='version', version=PROG) args = parser.parse_args() - + + # default no configuration available configobj = None - + + # 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") + + # read config direct from device via http if args.device is not None: - # read config direct from device via http buffer = io.BytesIO() url = str("http://{}/dl".format(args.device)) c = pycurl.Curl() @@ -1930,11 +2228,11 @@ if __name__ == "__main__": configobj = buffer.getvalue() + # read config from a file elif args.tasmotafile is not None: - # read config from a file if not os.path.isfile(args.tasmotafile): # check file exists - exit(1, "file '{}' not found".format(args.tasmotafile)) + exit(1, "File '{}' not found".format(args.tasmotafile)) try: tasmotafile = open(args.tasmotafile, "rb") configobj = tasmotafile.read() @@ -1942,22 +2240,59 @@ if __name__ == "__main__": except Exception, e: exit(e[0], e[1]) + # no config source given else: parser.print_help() sys.exit(0) + if configobj is not None and len(configobj)>0: cfg = DeEncrypt(configobj) - if args.outputfile is not None: - outputfile = open(args.outputfile, "wb") - outputfile.write(cfg) - outputfile.close() + config = Decode(cfg) - Decode(cfg) + # output to file + if args.outputfile is not None: + outputfilename = args.outputfile + v = f1 = f2 = f3 = f4 = '' + if 'version' in config: + ver = int(str(config['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 else '', subreleasestr) + outputfilename = outputfilename.replace('@v', v) + if 'friendlyname' in config: + outputfilename = outputfilename.replace('@f', config['friendlyname'][0] ) + + if args.outputfileformat == 'binary': + outputfile = open(outputfilename, "wb") + outputfile.write(struct.pack(' Date: Sat, 29 Sep 2018 16:55:53 +0200 Subject: [PATCH 07/18] Add basic support for color calibration --- sonoff/i18n.h | 1 + sonoff/settings.h | 4 +++- sonoff/settings.ino | 9 +++++++++ sonoff/sonoff_version.h | 2 +- sonoff/xdrv_04_light.ino | 34 +++++++++++++++++++++++++++++++--- 5 files changed, 45 insertions(+), 5 deletions(-) diff --git a/sonoff/i18n.h b/sonoff/i18n.h index 2090f4793..bc161c078 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -317,6 +317,7 @@ #define D_CMND_LEDTABLE "LedTable" #define D_CMND_FADE "Fade" #define D_CMND_PIXELS "Pixels" +#define D_CMND_RGBWWTABLE "RGBWWTable" #define D_CMND_ROTATION "Rotation" #define D_CMND_SCHEME "Scheme" #define D_CMND_SPEED "Speed" diff --git a/sonoff/settings.h b/sonoff/settings.h index bf862dd51..7dfb91089 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -322,7 +322,9 @@ struct SYSCFG { uint16_t mcp230xx_int_timer; // 718 - byte free_71A[174]; // 71A + uint8_t rgbwwTable[5]; // 71A + + byte free_71A[169]; // 71F unsigned long energy_frequency_calibration; // 7C8 diff --git a/sonoff/settings.ino b/sonoff/settings.ino index 95e6ce367..4116a1c6d 100644 --- a/sonoff/settings.ino +++ b/sonoff/settings.ino @@ -624,6 +624,10 @@ void SettingsDefaultSet2() Settings.button_debounce = KEY_DEBOUNCE_TIME; Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; + + for (byte j = 0; j < 5; j++) { + Settings.rgbwwTable[j] = 255; + } } /********************************************************************************************/ @@ -827,6 +831,11 @@ void SettingsDelta() Settings.button_debounce = KEY_DEBOUNCE_TIME; Settings.switch_debounce = SWITCH_DEBOUNCE_TIME; } + if (Settings.version < 0x0602010A) { + for (byte j = 0; j < 5; j++) { + Settings.rgbwwTable[j] = 255; + } + } Settings.version = VERSION; SettingsSave(1); diff --git a/sonoff/sonoff_version.h b/sonoff/sonoff_version.h index 4f2b5c0ec..c5765ec38 100644 --- a/sonoff/sonoff_version.h +++ b/sonoff/sonoff_version.h @@ -20,7 +20,7 @@ #ifndef _SONOFF_VERSION_H_ #define _SONOFF_VERSION_H_ -#define VERSION 0x06020109 +#define VERSION 0x0602010A #define D_PROGRAMNAME "Sonoff-Tasmota" #define D_AUTHOR "Theo Arends" diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index 2a5a15b0c..6c8aa938d 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -55,11 +55,11 @@ enum LightCommands { CMND_COLOR, CMND_COLORTEMPERATURE, CMND_DIMMER, CMND_LED, CMND_LEDTABLE, CMND_FADE, - CMND_PIXELS, CMND_ROTATION, CMND_SCHEME, CMND_SPEED, CMND_WAKEUP, CMND_WAKEUPDURATION, + CMND_PIXELS, CMND_RGBWWTABLE, CMND_ROTATION, CMND_SCHEME, CMND_SPEED, CMND_WAKEUP, CMND_WAKEUPDURATION, CMND_WIDTH, CMND_CHANNEL, CMND_HSBCOLOR, CMND_UNDOCA }; const char kLightCommands[] PROGMEM = D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_LED "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|" - D_CMND_PIXELS "|" D_CMND_ROTATION "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" + D_CMND_PIXELS "|" D_CMND_RGBWWTABLE "|" D_CMND_ROTATION "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" D_CMND_WIDTH "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR "|UNDOCA" ; struct LRgbColor { @@ -799,7 +799,8 @@ void LightAnimate() light_update = 0; for (byte i = 0; i < light_subtype; i++) { light_last_color[i] = light_new_color[i]; - cur_col[i] = (Settings.light_correction) ? ledTable[light_last_color[i]] : light_last_color[i]; + cur_col[i] = light_last_color[i]*Settings.rgbwwTable[i]/255; + cur_col[i] = (Settings.light_correction) ? ledTable[cur_col[i]] : cur_col[i]; if (light_type < LT_PWM6) { if (pin[GPIO_PWM1 +i] < 99) { if (cur_col[i] > 0xFC) { @@ -1279,6 +1280,33 @@ boolean LightCommand() } snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetStateText(Settings.light_correction)); } + else if (CMND_RGBWWTABLE == command_code) { + bool validtable = (XdrvMailbox.data_len > 0); + char scolor[25]; + if (validtable) { + uint16_t HSB[3]; + if (strstr(XdrvMailbox.data, ",")) { // Command with up to 5 comma separated parameters + for (int i = 0; i < LST_RGBWC; i++) { + char *substr; + + if (0 == i) { + substr = strtok(XdrvMailbox.data, ","); + } else { + substr = strtok(NULL, ","); + } + if (substr != NULL) { + Settings.rgbwwTable[i] = atoi(substr); + } + } + } + light_update = 1; + } + scolor[0] = '\0'; + for (byte i = 0; i < LST_RGBWC; i++) { + snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", Settings.rgbwwTable[i]); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, scolor); + } else if (CMND_FADE == command_code) { switch (XdrvMailbox.payload) { case 0: // Off From cdc8a4d9ebc6043db47aaf7f79a5f8132ad68350 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 30 Sep 2018 10:54:52 +0200 Subject: [PATCH 08/18] tiny change --- sonoff/settings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/settings.h b/sonoff/settings.h index 7dfb91089..978b72b24 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -324,7 +324,7 @@ struct SYSCFG { uint8_t rgbwwTable[5]; // 71A - byte free_71A[169]; // 71F + byte free_71F[169]; // 71F unsigned long energy_frequency_calibration; // 7C8 From 6a1a21f03a1d9fcaab3bfe81162a8eb1b575c778 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 30 Sep 2018 11:02:47 +0200 Subject: [PATCH 09/18] 6.2.1.10 Add RGBWWTable 6.2.1.10 20180930 * Add command RGBWWTable to support color calibration (#3933) --- sonoff/_changelog.ino | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 0a113a04f..d4c609f14 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,8 +1,11 @@ -/* 6.2.1.9 20180928 +/* 6.2.1.10 20180930 + * Add command RGBWWTable to support color calibration (#3933) + * + * 6.2.1.9 20180928 * Add Apparent Power and Reactive Power to Energy Monitoring devices (#251) * Add RF Receiver control to module MagicHome to be used on Arilux LC10 (#3792) * Fix I2CScan invalid JSON error message (#3925) - * Fix invalid configuration restores and decode_config.py crc error when savedata = 0 (#3918) + * Fix invalid configuration restores and decode_config.py crc error when savedata = 0 (#3918) * * 6.2.1.8 20180926 * Change status JSON message providing more switch and retain information From 2fd42446e99b0582545521f875f044ad6345f435 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 30 Sep 2018 14:33:26 +0200 Subject: [PATCH 10/18] Add ESP Switch support Add support for Michael Haustein ESP Switch --- sonoff/_changelog.ino | 3 ++- sonoff/sonoff_template.h | 43 +++++++++++++++++++++++++++++----------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index d4c609f14..20cbded76 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,6 +1,7 @@ /* 6.2.1.10 20180930 * Add command RGBWWTable to support color calibration (#3933) - * + * Add support for Michael Haustein ESP Switch + * * 6.2.1.9 20180928 * Add Apparent Power and Reactive Power to Energy Monitoring devices (#251) * Add RF Receiver control to module MagicHome to be used on Arilux LC10 (#3792) diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 55b1ce3aa..e51e918fe 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -232,6 +232,7 @@ enum SupportedModules { SHELLY2, PHILIPS, NEO_COOLCAM, + ESP_SWITCH, MAXMODULE }; /********************************************************************************************/ @@ -392,6 +393,7 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { SHELLY2, BLITZWOLF_BWSHP2, NEO_COOLCAM, + ESP_SWITCH, H801, MAGICHOME, ARILUX_LC01, @@ -598,21 +600,22 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_LED1, // GPIO16 Green/Blue Led (1 = On, 0 = Off) GPIO_ADC0 // ADC0 A0 Analog input }, - { "EXS Relay", // Latching relay (ESP8266) + { "EXS Relay(s)", // ES-Store Latching relay(s) (ESP8266) // https://ex-store.de/ESP8266-WiFi-Relay-V31 - // Module Pin 1 VCC 3V3, Module Pin 6 GND - GPIO_KEY1, // GPIO00 Module Pin 8 - Button (firmware flash) - GPIO_USER, // GPIO01 Module Pin 2 = UART0_TXD - GPIO_USER, // GPIO02 Module Pin 7 - GPIO_USER, // GPIO03 Module Pin 3 = UART0_RXD - GPIO_USER, // GPIO04 Module Pin 10 - GPIO_USER, // GPIO05 Module Pin 9 + // V3.1 Module Pin 1 VCC 3V3, Module Pin 6 GND + // https://ex-store.de/2-Kanal-WiFi-WLan-Relay-V5-Blackline-fuer-Unterputzmontage + GPIO_USER, // GPIO00 V3.1 Module Pin 8 - V5.0 Module Pin 4 + GPIO_USER, // GPIO01 UART0_TXD V3.1 Module Pin 2 - V5.0 Module Pin 3 + GPIO_USER, // GPIO02 V3.1 Module Pin 7 + GPIO_USER, // GPIO03 UART0_RXD V3.1 Module Pin 3 + GPIO_USER, // GPIO04 V3.1 Module Pin 10 - V5.0 Module Pin 2 + GPIO_USER, // GPIO05 V3.1 Module Pin 9 - V5.0 Module Pin 1 0, 0, 0, 0, 0, 0, // Flash connection GPIO_REL1, // GPIO12 Relay1 ( 1 = Off) GPIO_REL2, // GPIO13 Relay1 ( 1 = On) - GPIO_USER, // GPIO14 Module Pin 5 - 0, - GPIO_USER, // GPIO16 Module Pin 4 + GPIO_USER, // GPIO14 V3.1 Module Pin 5 - V5.0 Relay2 ( 1 = Off) + GPIO_LED1, // GPIO15 V5.0 LED1 + GPIO_USER, // GPIO16 V3.1 Module Pin 4 - V5.0 Relay2 ( 1 = On) 0 }, { "WiOn", // Indoor Tap (ESP8266) @@ -1061,12 +1064,28 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { { "Neo Coolcam", // Neo Coolcam (ESP8266) // https://www.banggood.com/NEO-COOLCAM-WiFi-Mini-Smart-Plug-APP-Remote-Control-Timing-Smart-Socket-EU-Plug-p-1288562.html?cur_warehouse=CN 0, 0, 0, 0, - GPIO_LED1_INV, // GPIO13 Red Led (0 = On, 1 = Off) + GPIO_LED1_INV, // GPIO04 Red Led (0 = On, 1 = Off) 0, 0, 0, 0, 0, 0, 0, // Flash connection GPIO_REL1, // GPIO12 Red Led and Relay (0 = Off, 1 = On) GPIO_KEY1, // GPIO13 Button 0, 0, 0, 0 + }, + { "ESP Switch", // Michael Haustein 4 channel wall switch (ESP07 = ESP8266) + // Use rules for further actions like - rule on power1#state do publish cmnd/other_device/power %value% endon + GPIO_KEY2, // GPIO00 Button 2 + GPIO_USER, // GPIO01 Serial RXD and Optional sensor + GPIO_REL3_INV, // GPIO02 Yellow Led 3 (0 = On, 1 = Off) + GPIO_USER, // GPIO03 Serial TXD and Optional sensor + GPIO_KEY1, // GPIO04 Button 1 + GPIO_REL2_INV, // GPIO05 Red Led 2 (0 = On, 1 = Off) + 0, 0, 0, 0, 0, 0, // Flash connection + GPIO_REL4_INV, // GPIO12 Blue Led 4 (0 = On, 1 = Off) + GPIO_KEY4, // GPIO13 Button 4 + GPIO_KEY3, // GPIO14 Button 3 + GPIO_LED1, // GPIO15 Optional sensor + GPIO_REL1_INV, // GPIO16 Green Led 1 (0 = On, 1 = Off) + 0 } }; From 191df17b13fa4e3c1204421d68060c8bca4f61b6 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 30 Sep 2018 16:52:25 +0200 Subject: [PATCH 11/18] Add support for EXS Relay V5.0 Add support for EXS Relay V5.0 (#3810) --- sonoff/_changelog.ino | 1 + sonoff/sonoff.ino | 35 +++++++++++++++++++---------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 20cbded76..da3db4570 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -1,6 +1,7 @@ /* 6.2.1.10 20180930 * Add command RGBWWTable to support color calibration (#3933) * Add support for Michael Haustein ESP Switch + * Add support for EXS Relay V5.0 (#3810) * * 6.2.1.9 20180928 * Add Apparent Power and Reactive Power to Energy Monitoring devices (#251) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 6b354bb24..b89ffe138 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -296,20 +296,23 @@ char* GetStateText(byte state) /********************************************************************************************/ -void SetLatchingRelay(power_t power, uint8_t state) +void SetLatchingRelay(power_t lpower, uint8_t state) { - power &= 1; - if (2 == state) { // Reset relay - state = 0; - latching_power = power; - latching_relay_pulse = 0; + // power xx00 - toggle REL1 (Off) and REL3 (Off) - device 1 Off, device 2 Off + // power xx01 - toggle REL2 (On) and REL3 (Off) - device 1 On, device 2 Off + // power xx10 - toggle REL1 (Off) and REL4 (On) - device 1 Off, device 2 On + // power xx11 - toggle REL2 (On) and REL4 (On) - device 1 On, device 2 On + + if (state && !latching_relay_pulse) { // Set latching relay to power if previous pulse has finished + latching_power = lpower; + latching_relay_pulse = 2; // max 200mS (initiated by stateloop()) } - else if (state && !latching_relay_pulse) { // Set port power to On - latching_power = power; - latching_relay_pulse = 2; // max 200mS (initiated by stateloop()) - } - if (pin[GPIO_REL1 +latching_power] < 99) { - digitalWrite(pin[GPIO_REL1 +latching_power], bitRead(rel_inverted, latching_power) ? !state : state); + + for (byte i = 0; i < devices_present; i++) { + uint8_t port = (i << 1) + ((latching_power >> i) &1); + if (pin[GPIO_REL1 +port] < 99) { + digitalWrite(pin[GPIO_REL1 +port], bitRead(rel_inverted, port) ? !state : state); + } } } @@ -2453,6 +2456,10 @@ void GpioInit() if (pin[GPIO_REL1 +i] < 99) { pinMode(pin[GPIO_REL1 +i], OUTPUT); devices_present++; + if (EXS_RELAY == Settings.module) { + digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? 1 : 0); + if (i &1) { devices_present--; } + } } } } @@ -2493,10 +2500,6 @@ void GpioInit() } } - if (EXS_RELAY == Settings.module) { - SetLatchingRelay(0,2); - SetLatchingRelay(1,2); - } SetLedPower(Settings.ledstate &8); XdrvCall(FUNC_PRE_INIT); From b9b6d132f53753987ef8a86e28aa104f03844512 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 30 Sep 2018 17:52:41 +0200 Subject: [PATCH 12/18] Fix timer offset -00:00 Fix timer offset -00:00 causing 12:00 hour offset (#3923) --- sonoff/_changelog.ino | 1 + sonoff/xdrv_09_timers.ino | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index da3db4570..21e00702e 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -2,6 +2,7 @@ * Add command RGBWWTable to support color calibration (#3933) * Add support for Michael Haustein ESP Switch * Add support for EXS Relay V5.0 (#3810) + * Fix timer offset -00:00 causing 12:00 hour offset (#3923) * * 6.2.1.9 20180928 * Add Apparent Power and Reactive Power to Energy Monitoring devices (#251) diff --git a/sonoff/xdrv_09_timers.ino b/sonoff/xdrv_09_timers.ino index 66bf93614..124b41534 100644 --- a/sonoff/xdrv_09_timers.ino +++ b/sonoff/xdrv_09_timers.ino @@ -194,7 +194,7 @@ void ApplyTimerOffsets(Timer *duskdawn) // apply offsets, check for over- and underflows uint16_t timeBuffer; - if ((uint16_t)stored.time > 720) { + if ((uint16_t)stored.time > 719) { // negative offset, time after 12:00 timeBuffer = (uint16_t)stored.time - 720; // check for underflow From 89825907d05b8e3816f22ad1194734b8bc9327da Mon Sep 17 00:00:00 2001 From: Florian Schroen Date: Sun, 30 Sep 2018 18:38:01 +0200 Subject: [PATCH 13/18] Add support for OBI smart socket Model tested: WFG-1 Models maybe working: WFF-1, WFE-1, WFI-1, WFD-1, WFR-1, WFA-1, WFU-1 Manufactured by: Cixi Yidong Electronics Co. Ltd - http://www.cn-yidong.com/ Sold by: OBI DIY market in Germany - https://www.obi.de/ --- sonoff/sonoff_template.h | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index e51e918fe..0a4f98d35 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -233,6 +233,7 @@ enum SupportedModules { PHILIPS, NEO_COOLCAM, ESP_SWITCH, + OBI, MAXMODULE }; /********************************************************************************************/ @@ -394,6 +395,7 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { BLITZWOLF_BWSHP2, NEO_COOLCAM, ESP_SWITCH, + OBI, H801, MAGICHOME, ARILUX_LC01, @@ -1085,7 +1087,21 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_KEY3, // GPIO14 Button 3 GPIO_LED1, // GPIO15 Optional sensor GPIO_REL1_INV, // GPIO16 Green Led 1 (0 = On, 1 = Off) - 0 + }, + { "OBI Socket", // OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko/p/2291706 + 0, // GPIO00 Flash jumper - not available + 0, // GPIO01 + 0, // GPIO02 + 0, // GPIO03 + GPIO_LED1, // GPIO04 LED on top and in switch button + GPIO_REL1, // GPIO05 Relay 1 (0 = Off, 1 = On) + 0, 0, 0, 0, 0, 0, // Flash connection + GPIO_LED2, // GPIO12 + 0, // GPIO13 + GPIO_KEY1, // GPIO14 switch button + 0, // GPIO15 + 0, // GPIO16 + 0 // GPIO17 } }; From ee0ef227dcf6830ae5c4a53ed7bb95f5b29d3aee Mon Sep 17 00:00:00 2001 From: andrethomas Date: Sun, 30 Sep 2018 23:07:46 +0200 Subject: [PATCH 14/18] Add ability to set default PWM Freq using #define #define USE_PCA9685_FREQ --- sonoff/user_config.h | 1 + sonoff/xdrv_15_pca9685.ino | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/sonoff/user_config.h b/sonoff/user_config.h index eceadc68d..72aa97d53 100644 --- a/sonoff/user_config.h +++ b/sonoff/user_config.h @@ -302,6 +302,7 @@ // #define USE_MCP230xx_DISPLAYOUTPUT // Enable MCP23008/MCP23017 to display state of OUTPUT pins on Web UI (+0k2 code) // #define USE_PCA9685 // Enable PCA9685 I2C HW PWM Driver - Must define I2C Address in #define USE_PCA9685_ADDR below - range 0x40 - 0x47 (+1k4 code) // #define USE_PCA9685_ADDR 0x40 // Enable PCA9685 I2C Address to use (Must be within range 0x40 through 0x47 - set according to your wired setup) +// #define USE_PCA9685_FREQ 50 // Define default PWM frequency in Hz to be used (must be within 24 to 1526) - If other value is used, it will rever to 50Hz // #define USE_MPR121 // Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code) // #define USE_CCS811 // Enable CCS811 sensor (I2C address 0x5A) (+2k2 code) // #define USE_MPU6050 // Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+2k6 code) diff --git a/sonoff/xdrv_15_pca9685.ino b/sonoff/xdrv_15_pca9685.ino index e53b8d17f..c17ec3bee 100644 --- a/sonoff/xdrv_15_pca9685.ino +++ b/sonoff/xdrv_15_pca9685.ino @@ -27,7 +27,7 @@ #define PCA9685_REG_PRE_SCALE 0xFE uint8_t pca9685_detected = 0; -uint16_t pca9685_freq = 50; +uint16_t pca9685_freq = USE_PCA9685_FREQ; void PCA9685_Detect(void) { @@ -51,7 +51,7 @@ void PCA9685_Detect(void) void PCA9685_Reset(void) { I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x80); - PCA9685_SetPWMfreq(50); + PCA9685_SetPWMfreq(USE_PCA9685_FREQ); for (uint8_t pin=0;pin<16;pin++) { PCA9685_SetPWM(pin,0,false); } @@ -63,9 +63,13 @@ void PCA9685_SetPWMfreq(double freq) { 7.3.5 from datasheet prescale value = round(25000000/(4096*freq))-1; */ - pca9685_freq=freq; - uint8_t pre_scale_osc = round(25000000/(4096*freq))-1; - if (1526 == freq) pre_scale_osc=0xFF; // force setting for 24hz because rounding causes 1526 to be 254 + if (freq > 23 && freq < 1527) { + pca9685_freq=freq; + } else { + pca9685_freq=50; + } + uint8_t pre_scale_osc = round(25000000/(4096*pca9685_freq))-1; + if (1526 == pca9685_freq) pre_scale_osc=0xFF; // force setting for 24hz because rounding causes 1526 to be 254 uint8_t current_mode1 = I2cRead8(USE_PCA9685_ADDR, PCA9685_REG_MODE1); // read current value of MODE1 register uint8_t sleep_mode1 = (current_mode1&0x7F) | 0x10; // Determine register value to put PCA to sleep I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, sleep_mode1); // Let's sleep a little From 068211cae9857bb93373690772f35b33b3f48c4d Mon Sep 17 00:00:00 2001 From: andrethomas Date: Sun, 30 Sep 2018 23:33:14 +0200 Subject: [PATCH 15/18] Add #define USE_PCA9685_FREQ 50 if not defined Add #define USE_PCA9685_FREQ 50 if not defined in user_config.h --- sonoff/xdrv_15_pca9685.ino | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sonoff/xdrv_15_pca9685.ino b/sonoff/xdrv_15_pca9685.ino index c17ec3bee..3114aa461 100644 --- a/sonoff/xdrv_15_pca9685.ino +++ b/sonoff/xdrv_15_pca9685.ino @@ -26,6 +26,10 @@ #define PCA9685_REG_LED0_ON_L 0x06 #define PCA9685_REG_PRE_SCALE 0xFE +#ifndef USE_PCA9685_FREQ + #define USE_PCA9685_FREQ 50 +#endif + uint8_t pca9685_detected = 0; uint16_t pca9685_freq = USE_PCA9685_FREQ; From da0b283fe7249e8b3b1c46f8808bb9a7251ff14f Mon Sep 17 00:00:00 2001 From: andrethomas Date: Mon, 1 Oct 2018 00:18:37 +0200 Subject: [PATCH 16/18] PCA9685 - Add telemetry output --- sonoff/xdrv_15_pca9685.ino | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/sonoff/xdrv_15_pca9685.ino b/sonoff/xdrv_15_pca9685.ino index 3114aa461..841ae22ab 100644 --- a/sonoff/xdrv_15_pca9685.ino +++ b/sonoff/xdrv_15_pca9685.ino @@ -32,6 +32,7 @@ uint8_t pca9685_detected = 0; uint16_t pca9685_freq = USE_PCA9685_FREQ; +uint16_t pca9685_pin_pwm_value[16]; void PCA9685_Detect(void) { @@ -57,7 +58,8 @@ void PCA9685_Reset(void) I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x80); PCA9685_SetPWMfreq(USE_PCA9685_FREQ); for (uint8_t pin=0;pin<16;pin++) { - PCA9685_SetPWM(pin,0,false); + PCA9685_SetPWM(pin,0,false); + pca9685_pin_pwm_value[pin] = 0; } snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"RESET\":\"OK\"}}")); } @@ -115,6 +117,7 @@ bool PCA9685_Command(void) if (',' == XdrvMailbox.data[ca]) { paramcount++; } } UpperCase(XdrvMailbox.data,XdrvMailbox.data); + if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"RESET")) { PCA9685_Reset(); return serviced; } if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"PWMF")) { @@ -122,7 +125,7 @@ bool PCA9685_Command(void) uint16_t new_freq = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2)); if ((new_freq >= 24) && (new_freq <= 1526)) { PCA9685_SetPWMfreq(new_freq); - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PWMF\":%i, \"Result\":\"OK\"}}")); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PWMF\":%i, \"Result\":\"OK\"}}"),new_freq); return serviced; } } else { // No parameter was given for setfreq, so we return current setting @@ -159,6 +162,17 @@ bool PCA9685_Command(void) return serviced; } +void PCA9685_OutputTelemetry(void) { + if (0 == pca9685_detected) { return; } // We do not do this if the PCA9685 has not been detected + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\",\"PCA9685\": {"), GetDateAndTime(DT_LOCAL).c_str()); + snprintf_P(mqtt_data,sizeof(mqtt_data), PSTR("%s\"PWM_FREQ\":%i,"),mqtt_data,pca9685_freq); + for (uint8_t pin=0;pin<16;pin++) { + snprintf_P(mqtt_data,sizeof(mqtt_data), PSTR("%s\"PWM%i\":%i,"),mqtt_data,pin,pca9685_pin_pwm_value[pin]); + } + snprintf_P(mqtt_data,sizeof(mqtt_data),PSTR("%s\"END\":1}}"),mqtt_data); + MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings.flag.mqtt_sensor_retain); +} + boolean Xdrv15(byte function) { boolean result = false; @@ -169,6 +183,9 @@ boolean Xdrv15(byte function) break; case FUNC_EVERY_SECOND: PCA9685_Detect(); + if (tele_period == 0) { + PCA9685_OutputTelemetry(); + } break; case FUNC_EVERY_50_MSECOND: break; From 047f430ad8c8ae65e8a376928b7792a88074810e Mon Sep 17 00:00:00 2001 From: andrethomas Date: Mon, 1 Oct 2018 00:27:16 +0200 Subject: [PATCH 17/18] PCA9685 - Remove unused callbacks in Xdrv15() --- sonoff/xdrv_15_pca9685.ino | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sonoff/xdrv_15_pca9685.ino b/sonoff/xdrv_15_pca9685.ino index 841ae22ab..9cb9292a0 100644 --- a/sonoff/xdrv_15_pca9685.ino +++ b/sonoff/xdrv_15_pca9685.ino @@ -179,25 +179,17 @@ boolean Xdrv15(byte function) if (i2c_flg) { switch (function) { - case FUNC_MQTT_DATA: - break; case FUNC_EVERY_SECOND: PCA9685_Detect(); if (tele_period == 0) { PCA9685_OutputTelemetry(); } break; - case FUNC_EVERY_50_MSECOND: - break; - case FUNC_JSON_APPEND: - break; case FUNC_COMMAND: if (XDRV_15 == XdrvMailbox.index) { PCA9685_Command(); } break; - case FUNC_WEB_APPEND: - break; default: break; } From 9e5aaef51f9cdf2cca08bd23e08aead10c03c7d9 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Mon, 1 Oct 2018 12:19:58 +0200 Subject: [PATCH 18/18] Add OBI/Teckin Socket support * Add support for OBI Power Socket (#1988, #3944) * Add support for Teckin Power Socket with Energy Monitoring (#3950) --- sonoff/_changelog.ino | 2 ++ sonoff/sonoff_template.h | 66 +++++++++++++++++++++++----------------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 21e00702e..04f209e10 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -3,6 +3,8 @@ * Add support for Michael Haustein ESP Switch * Add support for EXS Relay V5.0 (#3810) * Fix timer offset -00:00 causing 12:00 hour offset (#3923) + * Add support for OBI Power Socket (#1988, #3944) + * Add support for Teckin Power Socket with Energy Monitoring (#3950) * * 6.2.1.9 20180928 * Add Apparent Power and Reactive Power to Energy Monitoring devices (#251) diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 0a4f98d35..aad498f3b 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -234,6 +234,7 @@ enum SupportedModules { NEO_COOLCAM, ESP_SWITCH, OBI, + TECKIN, MAXMODULE }; /********************************************************************************************/ @@ -357,31 +358,31 @@ const uint8_t kGpioNiceList[GPIO_SENSOR_END] PROGMEM = { }; const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { - SONOFF_BASIC, + SONOFF_BASIC, // Sonoff Relay Devices SONOFF_RF, SONOFF_TH, SONOFF_DUAL, SONOFF_DUAL_R2, SONOFF_POW, SONOFF_POW_R2, - SONOFF_S31, SONOFF_4CH, SONOFF_4CHPRO, - SONOFF_SV, - SONOFF_DEV, - SONOFF_S2X, - SLAMPHER, - SONOFF_TOUCH, + SONOFF_S31, // Sonoff Socket Relay Devices with Energy Monitoring + SONOFF_S2X, // Sonoff Socket Relay Devices + SONOFF_TOUCH, // Sonoff Switch Devices SONOFF_T11, SONOFF_T12, SONOFF_T13, - SONOFF_SC, - SONOFF_B1, - SONOFF_LED, + SONOFF_LED, // Sonoff Light Devices SONOFF_BN, - SONOFF_IFAN02, - SONOFF_BRIDGE, - CH1, + SONOFF_B1, // Sonoff Light Bulbs + SLAMPHER, + SONOFF_SC, // Sonoff Environmemtal Sensor + SONOFF_IFAN02, // Sonoff Fan + SONOFF_BRIDGE, // Sonoff Bridge + SONOFF_SV, // Sonoff Development Devices + SONOFF_DEV, + CH1, // Relay Devices CH4, MOTOR, ELECTRODRAGON, @@ -392,11 +393,12 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { WION, SHELLY1, SHELLY2, - BLITZWOLF_BWSHP2, - NEO_COOLCAM, - ESP_SWITCH, + BLITZWOLF_BWSHP2, // Socket Relay Devices with Energy Monitoring + TECKIN, + NEO_COOLCAM, // Socket Relay Devices OBI, - H801, + ESP_SWITCH, // Switch Devices + H801, // Light Devices MAGICHOME, ARILUX_LC01, ARILUX_LC06, @@ -404,9 +406,9 @@ const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { ZENGGE_ZF_WF017, HUAFAN_SS, KMC_70011, - AILIGHT, + AILIGHT, // Light Bulbs PHILIPS, - WITTY, + WITTY, // Development Devices WEMOS }; @@ -615,9 +617,9 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { 0, 0, 0, 0, 0, 0, // Flash connection GPIO_REL1, // GPIO12 Relay1 ( 1 = Off) GPIO_REL2, // GPIO13 Relay1 ( 1 = On) - GPIO_USER, // GPIO14 V3.1 Module Pin 5 - V5.0 Relay2 ( 1 = Off) + GPIO_USER, // GPIO14 V3.1 Module Pin 5 - V5.0 GPIO_REL3_INV Relay2 ( 1 = Off) GPIO_LED1, // GPIO15 V5.0 LED1 - GPIO_USER, // GPIO16 V3.1 Module Pin 4 - V5.0 Relay2 ( 1 = On) + GPIO_USER, // GPIO16 V3.1 Module Pin 4 - V5.0 GPIO_REL4_INV Relay2 ( 1 = On) 0 }, { "WiOn", // Indoor Tap (ESP8266) @@ -1089,19 +1091,27 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { GPIO_REL1_INV, // GPIO16 Green Led 1 (0 = On, 1 = Off) }, { "OBI Socket", // OBI socket (ESP8266) - https://www.obi.de/hausfunksteuerung/wifi-stecker-schuko/p/2291706 - 0, // GPIO00 Flash jumper - not available - 0, // GPIO01 - 0, // GPIO02 - 0, // GPIO03 + 0, 0, 0, 0, GPIO_LED1, // GPIO04 LED on top and in switch button GPIO_REL1, // GPIO05 Relay 1 (0 = Off, 1 = On) 0, 0, 0, 0, 0, 0, // Flash connection GPIO_LED2, // GPIO12 0, // GPIO13 GPIO_KEY1, // GPIO14 switch button - 0, // GPIO15 - 0, // GPIO16 - 0 // GPIO17 + 0, 0, 0 + }, + { "Teckin", // https://www.amazon.de/gp/product/B07D5V139R + 0, + GPIO_KEY1, // GPIO01 Serial TXD and Button + 0, + GPIO_LED2_INV, // GPIO03 Serial RXD and Red Led (0 = On, 1 = Off) + GPIO_HLW_CF, // GPIO04 BL0937 or HJL-01 CF power + GPIO_HLW_CF1, // GPIO05 BL0937 or HJL-01 CF1 voltage / current + 0, 0, 0, 0, 0, 0, // Flash connection + GPIO_HLW_SEL, // GPIO12 BL0937 or HJL-01 Sel output + GPIO_LED1_INV, // GPIO13 Blue Led (0 = On, 1 = Off) + GPIO_REL1, // GPIO14 Relay (0 = Off, 1 = On) + 0, 0, 0 } };