From c5cb7ffb788cd192bc3fd9f4a04203ee8253eb01 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 26 Oct 2018 12:30:25 +0200 Subject: [PATCH 01/16] Add additional RFSend comand syntax Add additional RFSend comand syntax --- sonoff/xdrv_17_rcswitch.ino | 127 +++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 59 deletions(-) diff --git a/sonoff/xdrv_17_rcswitch.ino b/sonoff/xdrv_17_rcswitch.ino index 6c65dd3b0..e4cccb273 100644 --- a/sonoff/xdrv_17_rcswitch.ino +++ b/sonoff/xdrv_17_rcswitch.ino @@ -19,7 +19,7 @@ #ifdef USE_RC_SWITCH /*********************************************************************************************\ - * RF send and receive using RCSwitch library + * RF send and receive using RCSwitch library https://github.com/sui77/rc-switch/ \*********************************************************************************************/ #define D_JSON_RF_PROTOCOL "Protocol" @@ -34,39 +34,38 @@ RCSwitch mySwitch = RCSwitch(); -#define RF_TIME_AVOID_DUPLICATE 500 // Milliseconds +#define RF_TIME_AVOID_DUPLICATE 1000 // Milliseconds -unsigned long rf_lasttime = 0; +uint32_t rf_lasttime = 0; void RfReceiveCheck() { if (mySwitch.available()) { - unsigned long value = mySwitch.getReceivedValue(); - unsigned int bit_length = mySwitch.getReceivedBitlength(); - unsigned int delay = mySwitch.getReceivedDelay(); - unsigned int protocol = mySwitch.getReceivedProtocol(); + unsigned long data = mySwitch.getReceivedValue(); + unsigned int bits = mySwitch.getReceivedBitlength(); + int protocol = mySwitch.getReceivedProtocol(); + int delay = mySwitch.getReceivedDelay(); - snprintf_P(log_data, sizeof(log_data), PSTR("RFR: BitLen %d, Delay %d, Protocol %d, Value %lX (%u)"), - bit_length, delay, protocol, value, value); + snprintf_P(log_data, sizeof(log_data), PSTR("RFR: Data %lX (%u), Bits %d, Protocol %d, Delay %d"), data, data, bits, protocol, delay); AddLog(LOG_LEVEL_DEBUG); - unsigned long now = millis(); - if ((now - rf_lasttime > RF_TIME_AVOID_DUPLICATE) && (value > 0)) { + uint32_t now = millis(); + if ((now - rf_lasttime > RF_TIME_AVOID_DUPLICATE) && (data > 0)) { rf_lasttime = now; char stemp[16]; - if (Settings.flag.rf_receive_decimal) { - snprintf_P(stemp, sizeof(stemp), PSTR("%u"), (uint32_t)value); + if (Settings.flag.rf_receive_decimal) { // SetOption28 (0 = hexadecimal, 1 = decimal) + snprintf_P(stemp, sizeof(stemp), PSTR("%u"), (uint32_t)data); } else { - snprintf_P(stemp, sizeof(stemp), PSTR("\"%lX\""), (uint32_t)value); + snprintf_P(stemp, sizeof(stemp), PSTR("\"%lX\""), (uint32_t)data); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_RF_PROTOCOL "\":%d,\"" D_JSON_RF_BITS "\":%d,\"" D_JSON_RF_DATA "\":%s}}"), - protocol, bit_length, stemp); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_RF_DATA "\":%s,\"" D_JSON_RF_BITS "\":%d,\"" D_JSON_RF_PROTOCOL "\":%d}}"), + stemp, bits, protocol); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_RFRECEIVED)); XdrvRulesProcess(); #ifdef USE_DOMOTICZ - DomoticzSensor(DZ_COUNT, value); // Send value as Domoticz Counter value + DomoticzSensor(DZ_COUNT, data); // Send data as Domoticz Counter value #endif // USE_DOMOTICZ } mySwitch.resetAvailable(); @@ -87,63 +86,73 @@ void RfInit() * Commands \*********************************************************************************************/ -/* - * ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96 - RFsend: - { "protocol":1, "pulse":320, "repeat":15, "bits":24, "data":551502015 } -*/ - boolean RfSendCommand() { boolean serviced = true; - boolean error = false; - char dataBufUc[XdrvMailbox.data_len]; - uint32_t protocol = 0; - uint32_t pulse = 0; - uint32_t repeat = 0; - uint32_t bits = 0; - uint32_t data = 0; - - UpperCase(dataBufUc, XdrvMailbox.data); if (!strcasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_RFSEND))) { if (XdrvMailbox.data_len) { - StaticJsonBuffer<128> jsonBuf; + unsigned long data = 0; + unsigned int bits = 24; + int protocol = 1; + int repeat = 10; + int pulse = 350; + + char dataBufUc[XdrvMailbox.data_len]; + UpperCase(dataBufUc, XdrvMailbox.data); + StaticJsonBuffer<150> jsonBuf; // ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(5) + 40 = 134 JsonObject &root = jsonBuf.parseObject(dataBufUc); - if (!root.success()) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_RFSEND "\":\"" D_JSON_INVALID_JSON "\"}")); // JSON decode failed - } - else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_RFSEND "\":\"" D_JSON_DONE "\"}")); - + if (root.success()) { + // RFsend {"data":0x501014,"bits":24,"protocol":1,"repeat":10,"pulse":350} char parm_uc[10]; - protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PROTOCOL))]; - pulse = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PULSE))]; - repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_REPEAT))]; + data = strtoul(root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_DATA))], NULL, 0); // Allow decimal (5246996) and hexadecimal (0x501014) input bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_BITS))]; - data = strtoul(root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_DATA))], NULL, 0); - - if (!protocol) { protocol = 1; } - mySwitch.setProtocol(protocol); - if (!pulse) { pulse = 350; } // Default pulse length for protocol 1 - mySwitch.setPulseLength(pulse); - if (!repeat) { repeat = 10; } // Default at init - mySwitch.setRepeatTransmit(repeat); - if (!bits) { bits = 24; } // Default 24 bits - if (data) { - mySwitch.send(data, bits); - } - else { - error = true; + protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PROTOCOL))]; + repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_REPEAT))]; + pulse = root[UpperCase_P(parm_uc, PSTR(D_JSON_RF_PULSE))]; + } else { + // RFsend data, bits, protocol, repeat, pulse + char *p; + byte i = 0; + for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 5; str = strtok_r(NULL, ", ", &p)) { + switch (i++) { + case 0: + data = strtoul(str, NULL, 0); // Allow decimal (5246996) and hexadecimal (0x501014) input + break; + case 1: + bits = atoi(str); + break; + case 2: + protocol = atoi(str); + break; + case 3: + repeat = atoi(str); + break; + case 4: + pulse = atoi(str); + } } } - } - else { + + if (!protocol) { protocol = 1; } + mySwitch.setProtocol(protocol); + if (!pulse) { pulse = 350; } // Default pulse length for protocol 1 + mySwitch.setPulseLength(pulse); + if (!repeat) { repeat = 10; } // Default at init + mySwitch.setRepeatTransmit(repeat); + if (!bits) { bits = 24; } // Default 24 bits + if (data) { + mySwitch.send(data, bits); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_RFSEND "\":\"" D_JSON_DONE "\"}")); + } else { + error = true; + } + } else { error = true; } if (error) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_RFSEND "\":\"" D_JSON_NO " " D_JSON_RF_PROTOCOL ", " D_JSON_RF_PULSE ", " D_JSON_RF_REPEAT ", " D_JSON_RF_BITS " " D_JSON_OR " " D_JSON_RF_DATA "\"}")); + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_RFSEND "\":\"" D_JSON_NO " " D_JSON_RF_DATA ", " D_JSON_RF_BITS ", " D_JSON_RF_PROTOCOL ", " D_JSON_RF_REPEAT " " D_JSON_OR " " D_JSON_RF_PULSE "\"}")); } } else serviced = false; // Unknown command From 77d54be2673ccd8e298a54dbfc2cb2920ee28f93 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Fri, 26 Oct 2018 17:09:28 +0200 Subject: [PATCH 02/16] Prep for release Prep for release --- platformio.ini | 2 +- sonoff/sonoff_post.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 852db9128..4f86fd3ac 100644 --- a/platformio.ini +++ b/platformio.ini @@ -165,7 +165,7 @@ board = ${common.board} board_build.flash_mode = ${common.board_build.flash_mode} board_build.f_cpu = ${common.board_build.f_cpu} build_unflags = ${common.build_unflags} -build_flags = ${common.build_flags} -DUSE_ALL_SENSORS +build_flags = ${common.build_flags} -DUSE_SENSORS monitor_speed = ${common.monitor_speed} upload_port = ${common.upload_port} upload_resetmethod = ${common.upload_resetmethod} diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index 62b3fc88a..13824a262 100755 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -150,6 +150,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_RF_FLASH // Disable support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB #undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer #undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer +#undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code #endif // USE_CLASSIC @@ -247,6 +248,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_RF_FLASH // Disable support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB #undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer #undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer +#undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch #undef DEBUG_THEO // Disable debug code #undef USE_DEBUG_DRIVER // Disable debug code #endif // BE_MINIMAL From 4582e9573034ae58d12f13bb1394cb7d879dfece Mon Sep 17 00:00:00 2001 From: Jason2866 Date: Fri, 26 Oct 2018 22:36:45 +0200 Subject: [PATCH 03/16] Create RF-Bridge-EFM8BB1-20181023.hex --- .../fw_efm8bb1/RF-Bridge-EFM8BB1-20181023.hex | 492 ++++++++++++++++++ 1 file changed, 492 insertions(+) create mode 100644 tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20181023.hex diff --git a/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20181023.hex b/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20181023.hex new file mode 100644 index 000000000..e193e8efe --- /dev/null +++ b/tools/fw_efm8bb1/RF-Bridge-EFM8BB1-20181023.hex @@ -0,0 +1,492 @@ +:020000040000FA +:1000000002142AED24F8FEEFD39E4015ED2408FDDE +:10001000E433FCC3EF9DEC6480F874809850028058 +:1000200001C32202139A7597A522220218387EFF77 +:10003000EFD394004022EF9480501DE4FDED75F065 +:1000400005A4240CF582E4341EF583E493B507047B +:10005000AE0580040DBD06E5AF06220215CD121DCA +:100060004053D87853DBFE121D04E4900085F02243 +:10007000D2DE2202188BD202121ABAC290C296D2D3 +:1000800080E4FBFD7F10121CEB1206CE74A4F0D2AC +:10009000AFE4F53BF53CD296053CE53C7002053BF0 +:1000A000B410F3E53BB427EEC296120026300209E5 +:1000B0001219F48E3E8F3F8006753E01753F00E5B4 +:1000C0003F7004E53E640170409000CCE07007F59D +:1000D0003BF53C02028D053CE53C7002053BD394A8 +:1000E00010E53B94274002D296D3E53C9430E53BA3 +:1000F0009475500302028DE4F53BF53C9000CCF082 +:100100009000CBF0C29602028DE4F53BF53C9000E6 +:10011000CCE014602A14700302025F147003020220 +:100120001D1470030202332404600302028DE53FB4 +:1001300064AA600302028D9000CC04F002028DE5F7 +:100140003F9000CBF0A37402F0E53F120C3902059A +:10015000A00173A10185A501A9A601BDA701C6A89B +:1001600001E2A901C9B001D2B1019AC0028DFF001C +:100170000002141206879000A87401F0E490007544 +:10018000F07FA1806E12005E9000CC7404F0753D8B +:1001900008E4F54175400902028D9000CC7404F02A +:1001A000E4F54175400202028DE49000A8F0900051 +:1001B00075F07FA612071174A6F002028D1206CE0A +:1001C00074A4F002028D753D089000CC7403F00217 +:1001D000028D9000757401F07FB1120711EFF002EB +:1001E000028D1206879000A8E0F53AE4F0900075C1 +:1001F000F07FA91219B790007CEFF07D307C75126A +:10020000070002028D1206EE90007CEFF0E49000F1 +:10021000CCF08079E49000CBF0A3F08070E4F5415D +:10022000E53FF540E540D394009000CC402C7404A9 +:10023000F0805A74042541F582E43400F583E53FEB +:10024000F00541E541B540059000CC800DE541C386 +:10025000947040397540709000CC7402F0802EE5A7 +:100260003F645570289000CCF0C2029000CBE0248F +:1002700060601824FC600F24FE600B14600824F6F4 +:100280006004241070077FA0121CB8D2029000CB2B +:10029000E0120C3902B6A102E0A402F2A503DAA62C +:1002A00003F6A805C0A905FDB00670B103A6C003FA +:1002B000C8FF000000AA900084E030E70F7DC87CF2 +:1002C000001206B77FA312192E0205DA121CFC4099 +:1002D000030200AA7DE87C031206B77FA20205F59F +:1002E000900084E020E7030200AA7FA412192E02E6 +:1002F00003EE9000CCE060030200AA900085E024A9 +:10030000FC6070240460030200AA153D900008E020 +:10031000FEA3E0FF7C007D64120C5FC006C00712E4 +:100320001573D007D006120BA78F4B900006E0FE86 +:10033000A3E0FF8E4CF54D7C007D64120C5FC0067F +:10034000C007121573D007D006120BA78F4E754F3A +:1003500018900008E0FAA3E0FB900004E0FCA3E0A2 +:10036000FDA3E0FEA3E0FF1217569000747406F0A0 +:100370000204B97F0112002E12159960030200AA2F +:10038000E53D601A7F0112002EEF12154912158DFE +:1003900050030205B81206E2121D2E0205B812061D +:1003A000EE7FA00205F59000CCE060030200AA9069 +:1003B0000004E0FCA3E0FD120700D296121D2EC23D +:1003C000967FA0121CB8800A7F01121CB8E490002E +:1003D000CCF0D2021206F70200AA900084E020E7D7 +:1003E000030200AA547FF543FD7FA61214B9E490DE +:1003F0000084F00200AA9000CCE060030200AA9002 +:100400000085E024FC7003020569240460030200F7 +:10041000AA900004E0FF2480600804700DE4F51D3C +:100420008003751D017542FF800F12002E8F42E57B +:1004300042F46005121599F51D153D12005EE51D8B +:1004400014607C0460030200AA900004E0647F70E2 +:1004500024A31215BAA3E0FAA3E0FBA3E0F54BA393 +:10046000E0F54CA3E0F54DA3E0F54EA3E01207083C +:10047000740CF0803BE542F46031121547F5828E32 +:10048000831215AA740593FA740693FB740993F505 +:100490004B740793F54C740893F54D740A93F54E1D +:1004A000740B931207087401F08005E49000CBF000 +:1004B0009000CBE070030200AA121BF00200AA9089 +:1004C0000004E06480704E900074740CF090000B97 +:1004D000E0FF7E00900009E0FCA3E0FD120A47C0A7 +:1004E00006C00790000C1215C6C007C00690000D8C +:1004F0001215C6AA06AB0790000E1215C68E4F8FB6 +:10050000508A4D8B4ED04BD04C9000051215BA90AE +:10051000000FE08048E542F4700302062F121547F1 +:100520009000747401F08544828E83740612156401 +:10053000C006C0077407121564C007C0067408120D +:100540001564AA06AB0774091215648E4F8F508A82 +:100550004D8B4ED04BD04C1215AA740A93F551D046 +:1005600003D0021217D00200AAE53D70101206F760 +:100570007FA0121CB8D202E4F51D0200AAE51D14EA +:10058000601B0460030200AAE542F4600E121547E6 +:1005900012158D40061206E2121D2E801BE542F454 +:1005A000601612154712158D400EEFFD7C007F017D +:1005B0007E00121B0D121D2EE4900085F00200AA91 +:1005C000900084E0FF30E71C547FF5437DC87C0039 +:1005D00012069AAD437FAB1214B9E4900084F0D2B6 +:1005E000020200AA121CFC40030200AA7DE87C0360 +:1005F00012069A7FAA121CB8D2020200AA90000426 +:10060000E025E0F5439000CCE060030200AA9000F2 +:1006100085E0700512005E800B900085E064046048 +:10062000030200AAE5436007E540C394045008E4D0 +:100630009000CBF00200AA74062543F9E43400755B +:100640004801F549894AC3E540954324FEF54B909E +:100650000005E0F54C7B017A0079061216527FA066 +:10066000121CB890007CE0FF1219B7D2020200AA57 +:10067000900084E020E7030200AA7FB11216D4E4C0 +:10068000900084F00200AA7D327C007F017E00127F +:100690001B0DD296121D2EC296227F017E00121BC8 +:1006A0000DD296121D2EC2969000A8E53AF0900049 +:1006B0007CE0FF1219B7227F017E00121B0DD2963B +:1006C000121D2EC29690007CE0FF1219B7229000F6 +:1006D000A87401F0E4900075F07FA41219B790009F +:1006E0007C22EFFD7C007F017E00121B0D2290001A +:1006F0007CE0FF1219B72290007CE0FF1219B722AC +:100700007F017E00121B0D22F54F121756900074C8 +:10071000221219B790007C22AFE9AEEA120DB68E14 +:10072000088F092093030209A285080A85090BC3D3 +:10073000E509950DF511E508950CF510900075E0AB +:1007400014700302099C0460030209C3900085E051 +:100750001460650460030209C3900084E060030232 +:1007600009C39000A8E0FFAB11AA10AD0FAC0E12A8 +:1007700010558F1AE51A648070030209C390007F38 +:10078000120E6B900002E510F0A3E511120DC1E40A +:10079000900074F0900077F090007EF0F51BF51C4F +:1007A000FE7F70FD7B017A007904120C8CE49000CE +:1007B0007DF090008504F022E51A120D998E23F544 +:1007C00024E51A120DFA70030208D004600302092E +:1007D000C3900004120CBBFFD39400400B90007E2A +:1007E000E09F5004E004F022120E13AE10AF11ABE4 +:1007F00007AA06E50F2BFFE50E3AFEE433FDE433CE +:10080000FCC004A905AA06AB07AE0EAF0F120E730B +:10081000D000120BA78F22120D80FD120003401989 +:1008200090000A120D83FDAF22120003400B120C40 +:10083000B8120E646003020954120D80FDAF22123B +:1008400000035009120CB8C3120E444013D3E50F35 +:10085000951CE50E951B402B120CB8120E647023EC +:10086000900086120E6B120CC4C083C082120E0B55 +:100870007401A806088002C333D8FC4FD082D0830D +:10088000F0801790007A120E6BC290D3951CE50E83 +:10089000951B4006850E1B850F1C900088E0700D8F +:1008A000120CC6120D78FF121C4D120DBD120CB8A1 +:1008B000120E6460030209C3121CF45005E4900098 +:1008C00083F0120D7570030209941209D70209947E +:1008D000120CB8FF7E00900004120AE4FDACF01286 +:1008E0000A47120DE18E258F26120E13E4850F158F +:1008F000850E14F513F512851119851018F517F5E5 +:1009000016900006120CF81209C4500F900007123E +:100910000CF21209F55004C2908043900008120CAA +:10092000F21209C4502EAA23A9247BFF90000912B9 +:100930000CF81209F5501D120CC4C083C082120EAF +:100940000B7401A806088002C333D8FC4FD082D0B4 +:1009500083F0800AE4900084F0C290A3F02290001B +:1009600088E0700D120CC6120D78FF121C4D120D8E +:10097000BDAA23A9247BFF90000A120A08120E6464 +:100980007041121CF45005E4900083F0120D756064 +:10099000031209D7C290E4900085F022AF11AE1087 +:1009A000801E85080C85090DC3E509950BF50FE53B +:1009B00008950AF50E900075E0147007AF0FAE0EA3 +:1009C000120E9B22E5159FFFE5149EFE121CACC380 +:1009D000EF9526EE952522121D047D207C037F01D4 +:1009E0007E00121AE490007DE0900083F0A3E51AE7 +:1009F000F04480F022E5199FFFE5189EFE121CAC22 +:100A0000C3EF9526EE952522BB010CE58229F582E0 +:100A1000E5833AF583E0225006E92582F8E622BB19 +:100A2000FE06E92582F8E222E58229F582E5833A8D +:100A3000F583E49322BB010689828A83F022500267 +:100A4000F722BBFE01F322EF8DF0A4A8F0CF8CF0CB +:100A5000A428CE8DF0A42EFE22BC000BBE0029EFF0 +:100A60008DF084FFADF022E4CCF875F008EF2FFF95 +:100A7000EE33FEEC33FCEE9DEC984005FCEE9DFE63 +:100A80000FD5F0E9E4CEFD22EDF8F5F0EE8420D2AA +:100A90001CFEADF075F008EF2FFFED33FD40079819 +:100AA0005006D5F0F222C398FD0FD5F0EA22C2D548 +:100AB000EC30E709B2D5E4C39DFDE49CFCEE30E7E1 +:100AC00015B2D5E4C39FFFE49EFE120A59C3E49D0C +:100AD000FDE49CFC8003120A5930D507C3E49FFF54 +:100AE000E49EFE22BB0110E58229F582E5833AF5FA +:100AF00083E0F5F0A3E0225009E92582F886F008AA +:100B0000E622BBFE0AE92582F8E2F5F008E222E5DA +:100B1000832AF583E993F5F0A3E99322E88FF0A403 +:100B2000CC8BF0A42CFCE98EF0A42CFC8AF0EDA474 +:100B30002CFCEA8EF0A4CDA8F08BF0A42DCC3825A7 +:100B4000F0FDE98FF0A42CCD35F0FCEB8EF0A4FE87 +:100B5000A9F0EB8FF0A4CFC5F02ECD39FEE43CFC1C +:100B6000EAA42DCE35F0FDE43CFC2275F008758238 +:100B700000EF2FFFEE33FECD33CDCC33CCC5823327 +:100B8000C5829BED9AEC99E58298400CF582EE9B2C +:100B9000FEED9AFDEC99FC0FD5F0D6E4CEFBE4CD4A +:100BA000FAE4CCF9A88222B800C1B90059BA002DE4 +:100BB000EC8BF084CFCECDFCE5F0CBF97818EF2F9D +:100BC000FFEE33FEED33FDEC33FCEB33FB10D703CC +:100BD000994004EB99FB0FD8E5E4F9FA227818EF75 +:100BE0002FFFEE33FEED33FDEC33FCC933C910D7D4 +:100BF000059BE99A4007EC9BFCE99AF90FD8E0E4E1 +:100C0000C9FAE4CCFB2275F010EF2FFFEE33FEEDB6 +:100C100033FDCC33CCC833C810D7079BEC9AE89986 +:100C2000400AED9BFDEC9AFCE899F80FD5F0DAE468 +:100C3000CDFBE4CCFAE4C8F922D083D082F8E49367 +:100C40007012740193700DA3A393F8740193F5824D +:100C50008883E4737402936860EFA3A3A380DFEC3E +:100C60008EF0A4CCC5F0CCCDF8EFA4CEC5F02DFD10 +:100C7000E43CFCE8A42EC8C5F03DFDE43CFCEFA438 +:100C8000FFE5F028FEE43DFDE43CFC22EF4E60125F +:100C9000EF60010EEDBB010B89828A83F0A3DFFCBC +:100CA000DEFA2289F05007F709DFFCA9F022BBFE2B +:100CB000FCF309DFFCA9F02290000BAA23A9247BF6 +:100CC000FF020A08D290900077E024FFFFE434FF8F +:100CD000FE7C007D08120AAE74042FF58274003E7B +:100CE000F58322FF900074E02404F582E43400F5DB +:100CF0008322AA23A9247BFF120A08FD7C0090000E +:100D000004120AE4FFAEF0120A47C322120BA7AB8B +:100D100007AA06E4F9F87F407E427D0FFC120BA77C +:100D2000E47BFFFAF9F8120B1CA804A905AA06AB8C +:100D3000077F207ED77D757C01120BA7EFF404227C +:100D4000E53725E0248AF582E43400F58322AE2AD3 +:100D5000AF2B7C007D1F120A598E2E8F2F7C007DB9 +:100D600005120A59C3E52F9FFDE52E9EFCD3E52908 +:100D70009DE5289C22900083E0FF90007DE06F229B +:100D8000900009AA23A9247BFF020A0853DBFE5323 +:100D9000DAFE53F7DF53F7BF2275F005A4240EF5F2 +:100DA00082E4341EF583E4740193FA740293AE0274 +:100DB000227D64120A597C007D0A020A4790007D58 +:100DC000EFF09000887408F022C3E5379FFDE53608 +:100DD0009ED3FCE5339DE5329C2275F0FFA4FFAE67 +:100DE000F07C007D64020A59AA2EA92F7BFF900097 +:100DF00002120AE4F53785F0362275F005A4240DB9 +:100E0000F582E4341EF583E4931422E0FF90008819 +:100E1000E0FE22900088E014F0900077E004F022D9 +:100E2000E52F2FFFE52E3EFEC3E5299FE5289E22F4 +:100E3000AB48AA49A94A854D82758300020A0890E9 +:100E40000081E0D3FF900077E09F22900078E527B3 +:100E5000F0A3E528F022852F82852E83E493FA748F +:100E60000193FB22FF900077E06F22E50EF0A3E5EF +:100E70000FF0227C007D64020C5F540F75F002A419 +:100E8000F58285F083227B007A007929AF28AE278E +:100E900022D3E52B9494E52A9411228E278F289053 +:100EA0000085E0146035147003020F802402600393 +:100EB000021054C290900084E06003021054AF28E6 +:100EC000AE27121C7A4003021054E4900001F01285 +:100ED0000E4B9000857401F0D29022900078E0FCD7 +:100EE000A3E0FDAE047802CEC313CE13D8F92DFFD4 +:100EF000EC3EFED3E5289FE5279E4009120E4BE409 +:100F0000900001F022AF28AE271218DE501F90008B +:100F100001E094004017E4900074F090008874049D +:100F2000F0E490007DF09000857402F022C3E52883 +:100F30009464E52794005003020FE6120E86121106 +:100F4000BE5020E529120D42E0FEA3E02528FFE572 +:100F5000273EC313FEEF13FFE529120D42EEF0A367 +:100F6000EFF022900001E0120D42120E4E900001AF +:100F7000E004F0E0D3940F5003021054E402104553 +:100F8000B290AF28AE271218DE505E121CF4500546 +:100F9000E4900083F0120D75604A121D047DF47C0C +:100FA000017F017E00121AE490007DE0900083F042 +:100FB000900088E07019120CE4C083C082E0FF90BA +:100FC0000001E0540FFEEF4ED082D083F0800E90EF +:100FD0000001E0C454F0440F120CE3EFF0900084E1 +:100FE000E04480F0C290E48067120E8612126450D2 +:100FF00057900088E0B40410E529C454F0120CE3C3 +:10100000EFF0E4900088F022900074E0FF120CE80A +:10101000E0FEE529540FFDEE4DF074042F120CEAAA +:10102000120D78FF121C4DEFF0900074E004F09068 +:1010300000887404F0900074E0D394704016121D80 +:1010400004E4900001F08008121D04E4900001F017 +:10105000900085F0228F278C288D298A2A8B2B756A +:101060002C80E5277071E4F52D7F0112002EEF65CD +:101070002D701B120E914054E52B9450E52A944696 +:10108000504A120D4E4045120E2050408073E52DFF +:10109000120D998E2EF52FE52D120DFA60180470A1 +:1010A0002B120E56C002C003120DE8D003D002125C +:1010B000110750188013120E56C002C003120DE81B +:1010C000D003D0021211075003852D2C052DE52DDC +:1010D000C394064094802DAF2712002E8F2D7F01E0 +:1010E00012002EEF652D701C120E914017E52B9407 +:1010F00050E52A9446500D120D4E4008120E205015 +:1011000003852D2CAF2C22AD2BAC2AAF29AE281293 +:101110001113228E308F318C328D33C2007C007DD2 +:1011200064AF03AE02120DB38F828E83AE36AF373B +:10113000120DB1D3E58294F4E583940140050C7D52 +:10114000F48004AC83AD828C838D82D3EF94F4EE73 +:10115000940140067C017DF48004AC06AD07AE042A +:10116000AF05D3EB9400EA94004038EB9582FDEA9A +:101170009583FCD3E5319DE5309C403FEB2582FD16 +:10118000EA3583FCC3E5319DE5309C502E120DC934 +:101190004029E5372FFDE5363EC3120DD2501CD253 +:1011A000008018120DC94013E5372FFFE5363EFECB +:1011B000C3E5339FE5329E5002D200A200228E2A60 +:1011C0008F2B8B2C8A2D892EC2001212645005D2CF +:1011D00000021261E52BAE2A7803CEC313CE13D8DA +:1011E000F9FDAC06E52BAE2A7802CEC313CE13D898 +:1011F000F92DFDEE3CFCE52AC4F854F0C868FEE584 +:101200002BC4540F482DF531EC3EF530E4F52F900A +:101210000001E0FFE52FC39F5047C3E52B9531FF49 +:10122000E52A9530FEE52F120D42E0FCA3E0FDD348 +:101230009FEC9E4028E52B2531FFE52A3530FEC383 +:10124000ED9FEC9E5017E52E452D452C600BAB2CE9 +:10125000AA2DA92EE52F120A35D2008004052F8071 +:10126000AEA200228E328F338B348A358936C2018A +:10127000E4F537900001E0FFE537C39F4003021318 +:1012800005E533AE327803CEC313CE13D8F9FDACE7 +:1012900006E533AE327802CEC313CE13D8F92DF55E +:1012A00039EE3CF538120D40E0FEA3E0FFC395395E +:1012B000EE953850028004AE38AF398E388F39122F +:1012C0000D40E0FEA3E0FFC39539FDEE9538FCC369 +:1012D000ED9533EC95325028E5392FFFE5383EFE89 +:1012E000C3E5339FE5329E5017E5364535453460FA +:1012F0000BAB34AA35A936E537120A35D201800581 +:101300000537021273A201224200C700004200C347 +:1013100000004200C900004200C500004100CC00AE +:101320004100CB00011D00410084004100850041C7 +:1013300000A8004100750041007C00410076564144 +:101340000089AB410000004100820042007F0000A4 +:101350004200020000420086000042007A00004184 +:10136000008100410088004100770041007E00417B +:101370000074004100830041007D004100010041F4 +:1013800000C0004100C1004100BE004100BF00415B +:1013900000C2004100BD00C10300C0E0C0F0C08336 +:1013A000C082C0D075D000C000C001C002C003C060 +:1013B00004C005C006C007E5985403F55AF45298D6 +:1013C000E55A30E017121D499000C0121A52EFF092 +:1013D0009000C0E004F0E0B41402E4F0E55A30E11B +:1013E0002E9000C2E0D39400401A9000BFE0245B2E +:1013F000F8E6FF121D469000BFE004F09000C2E046 +:1014000014F08002D2039000BFE0B42002E4F0D0D8 +:1014100007D006D005D004D003D002D001D000D030 +:10142000D0D082D083D0F0D0E032121D4F787FE44C +:10143000F6D8FD75817A021474020076E493A3F85D +:10144000E493A34003F68001F208DFF48029E493DB +:10145000A3F85407240CC8C333C4540F4420C883D2 +:101460004004F456800146F6DFE4800B01020408D4 +:1014700010204080901308E47E019360BCA3FF54C9 +:101480003F30E509541FFEE493A360010ECF54C022 +:1014900025E060A840B8E493A3FAE493A3F8E493AA +:1014A000A3C8C582C8CAC583CAF0A3C8C582C8CAB2 +:1014B000C583CADFE9DEE780BE8F458D46E4FEFDC9 +:1014C000F547121A3F247AF582E4341EF58374013D +:1014D00093FA7402938A48F5497FAA121BB0AF456C +:1014E000121A3C2479F582E4341EF583E4931460E7 +:1014F0000E047019AA48A9497BFF90000B8009AA25 +:1015000048A9497BFF90000A120A08F547EEC395E7 +:1015100047500774082EFE0D80F3ED04FF121A3CAD +:101520002478F582E4341EF583E493FF121BB0E4C3 +:10153000FEEEC39D500974042E121A2A0E80F27F0B +:1015400055121BB0021D43E54275F005A424A2F517 +:1015500082E4341DF583740193FA740293AE028E13 +:1015600043F5442293FF7E00740493FC740593FDBD +:10157000020A47900006E0FEA3E0FF900009E02F7A +:10158000FF900008E03EAB07FAE4F9F822F5828EFE +:1015900083740C93FFD3940022EF75F005A424A16B +:1015A000F582E4341DF583E49322E493FE74019301 +:1015B000FF740293FC740393FD22E0FEA3E0FFA3FB +:1015C000E0FCA3E0FD22E0FF7E00020A47C0E0C08D +:1015D000F0C083C082C0D075D000C000C001C0027E +:1015E000C003C004C005C006C007E5D85487F52174 +:1015F000F452D8E5F730E508E5F730E603121A5D56 +:1016000053F7DFE52130E708E5D930E003121D503C +:10161000E52130E008E5DA30E003121B5DE521301A +:10162000E108E5DB30E003120718E52130E208E5C8 +:10163000DC30E003121D51D007D006D005D004D015 +:1016400003D002D001D000D0D0D082D083D0F0D04F +:10165000E0328B458A468947120D8C53E2FDE4F552 +:101660004DE54DC3954B504FAB45AA46A947C00326 +:10167000C002C001120E30C4120E7AD001D002D0C6 +:1016800003120AE4F54F85F04ED2801216C6AB4520 +:10169000AA46A947C003C002C001120E30120E7A3A +:1016A000D001D002D003120AE4F54F85F04EC2807B +:1016B0001216C6054D80AAB290AF4C154CEF709E25 +:1016C00043E202C29022FDAC4E7F0A7E00121A8EC7 +:1016D000121D2E22AE07E4F545121BA9900001E071 +:1016E00004FF121BB012174F900001E0FFE545C345 +:1016F0009F5012121A46121A2F121A46F583121A06 +:1017000036054580E3900078E0FF121BB09000782A +:10171000121A3612174FE4F545900074E02401FFC9 +:10172000E433FEC3E5459FEE6480F874809850175B +:1017300074042545121A2A0545E545541F70DA122E +:101740001D43121D3C80D27F55121BB0021D431257 +:101750001D43121D3C228E458F468C478D48AE029C +:10176000AF03120E73C007AF4BAB07E4FAF9F8D022 +:1017700007120D0C900000F0AE4CAF4D120E73C06E +:1017800007AF4EAB07E4FAF9F8D007120D0C900042 +:1017900082F0E54B120DDA900076EFF0E54E120D77 +:1017A000DA900089EFF090007FE545F0A3E546F080 +:1017B000900002E547F0A3E548F0900081E54FF086 +:1017C00043DA0153F7DF43F74053DBFE75F9FF229D +:1017D0008A498B4A755380120D8C53E2FD90007F2D +:1017E000EEF0A3EFF0900002ECF0A3EDF0121B3648 +:1017F000E4F552E552C395515032120CE4E05553D2 +:10180000600AAD50AC4FAF4EAE4D8008AD4CAC4B06 +:10181000AF4AAE49121BCCE553C313F55370099080 +:101820000074E004F0755380055280C743E202C2A1 +:10183000909000857404F022C0E0C083C082C0D0C4 +:1018400075D000C004C005C006C00753C87F900013 +:10185000C7E0FEA3E0FF4E700353C8FB9000C31225 +:1018600019AC50099000C7E4F0A3F0800DC39000BC +:10187000C8E09DF09000C7E09CF0D007D006D005EE +:10188000D004D0D0D082D083D0E032C0E0C083C0BA +:1018900082C0D075D000C004C005C006C0075391F7 +:1018A0007F9000C9E0FEA3E0FF4E70035391FB90D0 +:1018B00000C51219AC50099000C9E4F0A3F0800DE6 +:1018C000C39000CAE09DF09000C9E09CF0D007D022 +:1018D00006D005D004D0D0D082D083D0E032AB0780 +:1018E000AA06900078E0FEA3E0FF7C00120DB1D3C1 +:1018F000EF94F4EE940140050C7DF48004AC06AD49 +:1019000007AE04AF05C3900079E09FFD900078E03A +:101910009EFCC3ED9BEC9A5013A3E02FFF90007840 +:10192000E03EFEC3EB9FEA9E50028001C322AE0759 +:10193000E4FDF545121BA9900002E0FF121BB090D8 +:101940000002121A3690007AE0FF121BB090007A63 +:10195000121A36900086E0FF121BB0900086121A11 +:101960003674042D121A2A0DBD03F67F55121BB0D2 +:10197000021D43AB07AA06E4F9F87F407E427D0FC3 +:10198000FC120BA7A804A905AA06AB077F207ED7E7 +:101990007D757C01120BA7C3E49FFFE49EFE22AB82 +:1019A00007AA06E4F9F87FE87E03FD22E0FCA3E045 +:1019B000FDC3EF9DEE9C228F4590007CE0F5467FB5 +:1019C0000B121D4C43DB01120D8F1200707D0A7C3F +:1019D000007F017E00121B0D121D2E43E202E490D7 +:1019E0000085F0900084F09000CBE545F090007CFD +:1019F000F0AF46229000C0E0FF9000BEE0B50705C2 +:101A00007E017F00229000BE121A52E0FD7C009001 +:101A100000BEE004F0E0B41402E4F09000BDE0FE8B +:101A2000EE4204E4F0AE04AF0522F582E43400F5A2 +:101A300083E0FF021BB0A3E0FF021BB0121BB0E566 +:101A40004675F005A422E54525E0248AF582E434B4 +:101A50000022E024A9F582E43400F58322120E3F2F +:101A60004003021C2F120CE4120E0BEFA806088094 +:101A700002C313D8FC30E00B900000E0FF121D4CB5 +:101A8000D29022900082E0FF121D4CC290228E560E +:101A90008F578C588D59121973121C94E55924BF15 +:101AA0009000CAF0E55834FF9000C9F09000C5E5F9 +:101AB00056F0A3E557F04391042212002A121D0BA1 +:101AC000121D12121CC3121D34121C64121CE112CE +:101AD0001CCD121CD7121CA0121D19121D20121D84 +:101AE00038021D278E2A8F2B8C2C8D2D12199F12B8 +:101AF0001980121C889000C7E52CF0A3E52DF0900A +:101B000000C3E52AF0A3E52BF043C804228E478FDB +:101B1000488C498D4A12199F121980121C9490000A +:101B2000C9E549F0A3E54AF09000C5E547F0A3E513 +:101B300048F043910422D290D28090007F121B5033 +:101B4000121D2EC290C280900002121B50021D2E48 +:101B5000E0FCA3E0FD7F0A7E00121A8E229000882E +:101B6000E07008900074E004120DC1120E1990008C +:101B700088E014F0120E3F5003021B8253E2FDC2B4 +:101B80008022120CE4120E0BEFA806088002C31389 +:101B9000D8FC30E0059000768003900089E07D005D +:101BA000FCE4FF121C1122AE077FAA121BB0AF0685 +:101BB000C2039000C1E0B42002E4F09000C1E02430 +:101BC0005BF8A607E004F0A3E004F0228C548D55E6 +:101BD000D280AD07AC067F0A7E00121A8E121D2E2F +:101BE000C280AD55AC547F0A7E00121A8E021D2EA3 +:101BF000120DC2E014F09000777401F0900085742B +:101C000003F0121B8253E2FD121B3643E202020074 +:101C100070AB07AF04EB14600C14600E2402700E5E +:101C20008DFB8FFC228DE98FEA228DEB8FEC22E475 +:101C3000FDFCFF121C11120D8F121D4053D878535A +:101C4000E2FDC280C2909000857404F0227E1DE403 +:101C5000FDEF30E70625E06EFF8004EF25E0FF0D85 +:101C6000BD08EE22AF885388AF758CA0758DCBEF81 +:101C70005440FEEF54104E428822C3EF94ACEE94D1 +:101C80000D4003D38001C322AD07AC06ECF5CBAF0A +:101C9000058FCA22AD07AC06ECF593AF058F9222F3 +:101CA000C2DE75D90575F9FF75960122EE30E7079A +:101CB000C3E49FFFE49EFE22121BA77F55121BB0B8 +:101CC000021D4375E34075E10175E20222E591547E +:101CD000045391FB429122758E547589224388509A +:101CE00022E5C8540453C8FB42C82253984FEB4F17 +:101CF0004DF59822E5C8C320E201D322E591C32027 +:101D0000E201D32253C8FB53C87F2275A41175D4B6 +:101D1000CE2275A54175D5772253F77F75DA4A2211 +:101D200053F77F75DB302275E69075A8B022E591F8 +:101D300020E2FB22E4F5A922439810223003FD2281 +:101D4000C2DE22D299228F9922AF99228F8C222231 +:101D500022220190307000064001904B19180012A9 +:101D6000C005DC0002BC012C461E28080BB8232845 +:101D700000044C01904B191800251C0BB80003847B +:101D80000140461E1800000072D80702BC012C2634 +:101D90004040000BB81C520190010303011846009B +:101DA0000100FF1D520200FF1D5F0300FF1D6C04B8 +:101DB00000FF1D790500FF1D860601FF1D930190A0 +:101DC000307000064001904B19180012C005DC006D +:101DD00002BC012C461E28080BB8232800044C0125 +:101DE000904B191800251C0BB80003840140461EB7 +:101DF0001800000072D80702BC012C264040000BDE +:101E0000B81C520190010303011846000100FF1D98 +:101E1000BE0200FF1DCB0300FF1DD80400FF1DE51F +:101E20000500FF1DF20601FF1DFF01903070000646 +:101E30004001904B19180012C005DC0002BC012CB7 +:101E4000461E28080BB8232800044C01904B191893 +:101E500000251C0BB80003840140461E180000003A +:101E600072D80702BC012C264040000BB81C52015E +:101E700090010303011846000100FF1E2A0200FF23 +:101E80001E370300FF1E440400FF1E510500FF1E05 +:061E90005E0601FF1E6B5F +:00000001FF From 06cc70aaf4f38c2edb14e17c3635e9cce7a65bca Mon Sep 17 00:00:00 2001 From: Adrian Scillato <35405447+ascillato@users.noreply.github.com> Date: Fri, 26 Oct 2018 19:01:36 -0300 Subject: [PATCH 04/16] Fix Alexa for ALL cores Alexa uses Phillips Hue Emulation to control Tasmota. With this fix, the webserver arguments are passed to Tasmota as keys instead of args solving the Alexa issue for ALL cores. Besides, for core 2.6.0 is planned to deprecate the use of args in favor of keys. This fix makes Alexa to works compiling Tasmota under core 2.3.0, 2.4.0, 2.4.1, 2.4.2, 2.5.0 (stage), 2.6.0 (planned) Tested Ok. --- sonoff/xplg_wemohue.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/xplg_wemohue.ino b/sonoff/xplg_wemohue.ino index f8eff5ddc..2f70174f0 100644 --- a/sonoff/xplg_wemohue.ino +++ b/sonoff/xplg_wemohue.ino @@ -664,7 +664,7 @@ void HueLights(String *path) response = "["; StaticJsonBuffer<400> jsonBuffer; - JsonObject &hue_json = jsonBuffer.parseObject(WebServer->arg((WebServer->args())-1)); + JsonObject &hue_json = jsonBuffer.parseObject(WebServer->arg("1")); if (hue_json.containsKey("on")) { response += FPSTR(HUE_LIGHT_RESPONSE_JSON); From 22c20ab5feb5f3211c0182775cb29d331cdf17da Mon Sep 17 00:00:00 2001 From: Joel Stein Date: Sat, 27 Oct 2018 03:01:09 +0200 Subject: [PATCH 05/16] tuya: software-serial --- sonoff/language/bg-BG.h | 2 + sonoff/language/cs-CZ.h | 2 + sonoff/language/de-DE.h | 2 + sonoff/language/el-GR.h | 2 + sonoff/language/en-GB.h | 2 + sonoff/language/es-AR.h | 2 + sonoff/language/fr-FR.h | 2 + sonoff/language/he-HE.h | 2 + sonoff/language/hu-HU.h | 2 + sonoff/language/it-IT.h | 2 + sonoff/language/nl-NL.h | 2 + sonoff/language/pl-PL.h | 2 + sonoff/language/pt-BR.h | 2 + sonoff/language/pt-PT.h | 2 + sonoff/language/ru-RU.h | 2 + sonoff/language/tr-TR.h | 2 + sonoff/language/uk-UK.h | 2 + sonoff/language/zh-CN.h | 2 + sonoff/language/zh-TW.h | 2 + sonoff/sonoff_template.h | 19 ++++--- sonoff/xdrv_16_tuyadimmer.ino | 96 ++++++++++++++++++++--------------- 21 files changed, 106 insertions(+), 47 deletions(-) diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h index 7f56e33d3..7e096f673 100644 --- a/sonoff/language/bg-BG.h +++ b/sonoff/language/bg-BG.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h index a29f5741a..0cf34043b 100644 --- a/sonoff/language/cs-CZ.h +++ b/sonoff/language/cs-CZ.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 9f11e57ca..6a783a45b 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/el-GR.h b/sonoff/language/el-GR.h index beaaef57d..937464699 100644 --- a/sonoff/language/el-GR.h +++ b/sonoff/language/el-GR.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index 4e3aa0080..925080d3d 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-AR.h index 39f198681..d78c807da 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-AR.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index 44ecea2b1..bb32c2d2e 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h index 73b44c67b..d1aee3423 100644 --- a/sonoff/language/he-HE.h +++ b/sonoff/language/he-HE.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h index 532d45643..91b005e39 100644 --- a/sonoff/language/hu-HU.h +++ b/sonoff/language/hu-HU.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index 61f361b89..2ed03ca35 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index d2e913357..ca7be61af 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index 0f1303b30..3fd26f29b 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h index 54d8448d7..dceadf085 100644 --- a/sonoff/language/pt-BR.h +++ b/sonoff/language/pt-BR.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index 0c8e03d62..a7b9236ae 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index 11fdf90b0..fc2dc2892 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "А" diff --git a/sonoff/language/tr-TR.h b/sonoff/language/tr-TR.h index 86838d0df..ea3f10ce6 100755 --- a/sonoff/language/tr-TR.h +++ b/sonoff/language/tr-TR.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/uk-UK.h b/sonoff/language/uk-UK.h index efc125351..cf9d8bcf3 100644 --- a/sonoff/language/uk-UK.h +++ b/sonoff/language/uk-UK.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "А" diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index 3414bc8d1..15708dded 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "安" diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h index b5f14dace..75c682396 100644 --- a/sonoff/language/zh-TW.h +++ b/sonoff/language/zh-TW.h @@ -527,6 +527,8 @@ #define D_SENSOR_TX20_TX "TX20" #define D_SENSOR_RFSEND "RFSend" #define D_SENSOR_RFRECV "RFrecv" +#define D_SENSOR_TUYA_TX "Tuya Tx" +#define D_SENSOR_TUYA_RX "Tuya Rx" // Units #define D_UNIT_AMPERE "安" diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 13a6ffd0c..21227d379 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -128,9 +128,11 @@ enum UserSelectablePins { GPIO_SDS0X1_TX, // Nova Fitness SDS011 Serial interface GPIO_HX711_SCK, // HX711 Load Cell clock GPIO_HX711_DAT, // HX711 Load Cell data - GPIO_TX20_TXD_BLACK, // TX20 Transmission Pin + GPIO_TX20_TXD_BLACK, // TX20 Transmission Pin GPIO_RFSEND, // RF transmitter GPIO_RFRECV, // RF receiver + GPIO_TUYA_TX, // Tuya Serial interface + GPIO_TUYA_RX, // Tuya Serial interface GPIO_SENSOR_END }; // Programmer selectable GPIO functionality offset by user selectable GPIOs @@ -187,7 +189,8 @@ const char kSensorNames[] PROGMEM = D_SENSOR_DFR562 "|" D_SENSOR_SDS0X1_TX "|" D_SENSOR_HX711_SCK "|" D_SENSOR_HX711_DAT "|" D_SENSOR_TX20_TX "|" - D_SENSOR_RFSEND "|" D_SENSOR_RFRECV; + D_SENSOR_RFSEND "|" D_SENSOR_RFRECV "|" + D_SENSOR_TUYA_TX "|" D_SENSOR_TUYA_RX; /********************************************************************************************/ @@ -371,7 +374,9 @@ const uint8_t kGpioNiceList[GPIO_SENSOR_END] PROGMEM = { GPIO_SDM630_RX, // SDM630 Serial interface GPIO_PMS5003, // Plantower PMS5003 Serial interface GPIO_TX20_TXD_BLACK, // TX20 Transmission Pin - GPIO_MP3_DFR562 // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface + GPIO_MP3_DFR562, // RB-DFR-562, DFPlayer Mini MP3 Player Serial interface + GPIO_TUYA_TX, // Tuya Serial interface + GPIO_TUYA_RX // Tuya Serial interface }; const uint8_t kModuleNiceList[MAXMODULE] PROGMEM = { @@ -1150,12 +1155,14 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { { "Tuya Dimmer", // Tuya Dimmer (ESP8266 w/ separate MCU dimmer) // https://www.amazon.com/gp/product/B07CTNSZZ8/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1 GPIO_KEY1, // Virtual Button (controlled by MCU) - GPIO_TXD, // GPIO01 MCU serial control + GPIO_TXD, // GPIO01 MCU serial control GPIO_USER, - GPIO_RXD, // GPIO03 MCU serial control + GPIO_RXD, // GPIO03 MCU serial control GPIO_USER, GPIO_USER, - 0, 0, 0, 0, 0, 0, // Flash connection + 0, 0, + 0, + 0, 0, 0, // Flash connection GPIO_USER, GPIO_USER, GPIO_LED1, // GPIO14 Green Led diff --git a/sonoff/xdrv_16_tuyadimmer.ino b/sonoff/xdrv_16_tuyadimmer.ino index 1a5776d4a..6ffa789b2 100644 --- a/sonoff/xdrv_16_tuyadimmer.ino +++ b/sonoff/xdrv_16_tuyadimmer.ino @@ -23,6 +23,10 @@ #define TUYA_DIMMER_ID 3 #endif +#include + +TasmotaSerial *TuyaSerial; + uint8_t tuya_new_dim = 0; // Tuya dimmer value temp boolean tuya_ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction uint8_t tuya_cmd_status = 0; // Current status of serial-read @@ -41,19 +45,19 @@ boolean TuyaSetPower() snprintf_P(log_data, sizeof(log_data), PSTR("TYA: SetDevicePower.rpower=%d"), rpower); AddLog(LOG_LEVEL_DEBUG); - Serial.write(0x55); // Tuya header 55AA - Serial.write(0xAA); - Serial.write(0x00); // version 00 - Serial.write(0x06); // Tuya command 06 - Serial.write(0x00); - Serial.write(0x05); // following data length 0x05 - Serial.write(0x01); // relay number 1,2,3 - Serial.write(0x01); - Serial.write(0x00); - Serial.write(0x01); - Serial.write(rpower); // status - Serial.write(0x0D + rpower); // checksum sum of all bytes in packet mod 256 - Serial.flush(); + TuyaSerial->write((uint8_t)0x55); // Tuya header 55AA + TuyaSerial->write((uint8_t)0xAA); + TuyaSerial->write((uint8_t)0x00); // version 00 + TuyaSerial->write((uint8_t)0x06); // Tuya command 06 + TuyaSerial->write((uint8_t)0x00); + TuyaSerial->write((uint8_t)0x05); // following data length 0x05 + TuyaSerial->write((uint8_t)0x01); // relay number 1,2,3 + TuyaSerial->write((uint8_t)0x01); + TuyaSerial->write((uint8_t)0x00); + TuyaSerial->write((uint8_t)0x01); + TuyaSerial->write((uint8_t)rpower); // status + TuyaSerial->write((uint8_t)0x0D + rpower); // checksum sum of all bytes in packet mod 256 + TuyaSerial->flush(); status = true; } @@ -66,22 +70,22 @@ void LightSerialDuty(uint8_t duty) if (duty < 25) { duty = 25; // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself } - Serial.write(0x55); // Tuya header 55AA - Serial.write(0xAA); - Serial.write(0x00); // version 00 - Serial.write(0x06); // Tuya command 06 - send order - Serial.write(0x00); - Serial.write(0x08); // following data length 0x08 - Serial.write(Settings.param[P_TUYA_DIMMER_ID]); // dimmer id - Serial.write(0x02); // type=value - Serial.write(0x00); // length hi - Serial.write(0x04); // length low - Serial.write(0x00); // - Serial.write(0x00); // - Serial.write(0x00); // - Serial.write( duty ); // dim value (0-255) - Serial.write( byte(Settings.param[P_TUYA_DIMMER_ID] + 19 + duty) ); // checksum:sum of all bytes in packet mod 256 - Serial.flush(); + TuyaSerial->write((uint8_t)0x55); // Tuya header 55AA + TuyaSerial->write((uint8_t)0xAA); + TuyaSerial->write((uint8_t)0x00); // version 00 + TuyaSerial->write((uint8_t)0x06); // Tuya command 06 - send order + TuyaSerial->write((uint8_t)0x00); + TuyaSerial->write((uint8_t)0x08); // following data length 0x08 + TuyaSerial->write((uint8_t)Settings.param[P_TUYA_DIMMER_ID]); // dimmer id + TuyaSerial->write((uint8_t)0x02); // type=value + TuyaSerial->write((uint8_t)0x00); // length hi + TuyaSerial->write((uint8_t)0x04); // length low + TuyaSerial->write((uint8_t)0x00); // + TuyaSerial->write((uint8_t)0x00); // + TuyaSerial->write((uint8_t)0x00); // + TuyaSerial->write((uint8_t) duty ); // dim value (0-255) + TuyaSerial->write((uint8_t) byte(Settings.param[P_TUYA_DIMMER_ID] + 19 + duty) ); // checksum:sum of all bytes in packet mod 256 + TuyaSerial->flush(); snprintf_P(log_data, sizeof(log_data), PSTR( "TYA: Send Serial Packet Dim Value=%d (id=%d)"), duty, Settings.param[P_TUYA_DIMMER_ID]); AddLog(LOG_LEVEL_DEBUG); @@ -145,9 +149,9 @@ void TuyaPacketProcess() void TuyaSerialInput() { - while (Serial.available()) { + while (TuyaSerial->available()) { yield(); - serial_in_byte = Serial.read(); + serial_in_byte = TuyaSerial->read(); //snprintf_P(log_data, sizeof(log_data), PSTR("TYA: serial_in_byte %d, tuya_cmd_status %d, tuya_cmd_checksum %d, tuya_data_len %d, serial_in_byte_counter %d"), serial_in_byte, tuya_cmd_status, tuya_cmd_checksum, tuya_data_len, serial_in_byte_counter); //AddLog(LOG_LEVEL_DEBUG); @@ -215,20 +219,30 @@ void TuyaInit() if (!Settings.param[P_TUYA_DIMMER_ID]) { Settings.param[P_TUYA_DIMMER_ID] = TUYA_DIMMER_ID; } - Serial.setDebugOutput(false); - ClaimSerial(); + if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { + pin[GPIO_TUYA_RX] = pin[GPIO_RXD]; + pin[GPIO_TUYA_TX] = pin[GPIO_TXD]; + } + TuyaSerial = new TasmotaSerial(pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX], 1); + if (TuyaSerial->begin(baudrate)) { + if (TuyaSerial->hardwareSerial()) { + ClaimSerial(); + Serial.setDebugOutput(false); + } + } // Get current status of MCU snprintf_P(log_data, sizeof(log_data), "TYA: Request MCU state"); AddLog(LOG_LEVEL_DEBUG); - Serial.write(0x55); // header 55AA - Serial.write(0xAA); - Serial.write(0x00); // version 00 - Serial.write(0x08); // command 08 - get status - Serial.write(0x00); - Serial.write(0x00); // following data length 0x00 - Serial.write(0x07); // checksum:sum of all bytes in packet mod 256 - Serial.flush(); + + TuyaSerial->write((uint8_t)0x55); // header 55AA + TuyaSerial->write((uint8_t)0xAA); + TuyaSerial->write((uint8_t)0x00); // version 00 + TuyaSerial->write((uint8_t)0x08); // command 08 - get status + TuyaSerial->write((uint8_t)0x00); + TuyaSerial->write((uint8_t)0x00); // following data length 0x00 + TuyaSerial->write((uint8_t)0x07); // checksum:sum of all bytes in packet mod 256 + TuyaSerial->flush(); } boolean TuyaButtonPressed() From 7093bd6ab0c4565a8b4a71dd959079fa2d20d163 Mon Sep 17 00:00:00 2001 From: Joel Stein Date: Sat, 27 Oct 2018 04:11:30 +0200 Subject: [PATCH 06/16] tuya: software-serial: fixes --- sonoff/sonoff_template.h | 6 +++--- sonoff/xdrv_16_tuyadimmer.ino | 34 +++++++++++++++++++--------------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index 21227d379..a071dd8c7 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -1155,13 +1155,13 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { { "Tuya Dimmer", // Tuya Dimmer (ESP8266 w/ separate MCU dimmer) // https://www.amazon.com/gp/product/B07CTNSZZ8/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1 GPIO_KEY1, // Virtual Button (controlled by MCU) - GPIO_TXD, // GPIO01 MCU serial control + GPIO_USER, // GPIO01 MCU serial control GPIO_USER, - GPIO_RXD, // GPIO03 MCU serial control + GPIO_USER, // GPIO03 MCU serial control GPIO_USER, GPIO_USER, 0, 0, - 0, + GPIO_USER, 0, 0, 0, // Flash connection GPIO_USER, GPIO_USER, diff --git a/sonoff/xdrv_16_tuyadimmer.ino b/sonoff/xdrv_16_tuyadimmer.ino index 6ffa789b2..e810aa26d 100644 --- a/sonoff/xdrv_16_tuyadimmer.ino +++ b/sonoff/xdrv_16_tuyadimmer.ino @@ -25,7 +25,7 @@ #include -TasmotaSerial *TuyaSerial; +TasmotaSerial *TuyaSerial = nullptr; uint8_t tuya_new_dim = 0; // Tuya dimmer value temp boolean tuya_ignore_dim = false; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction @@ -40,7 +40,7 @@ boolean TuyaSetPower() uint8_t rpower = XdrvMailbox.index; int16_t source = XdrvMailbox.payload; - if (source != SRC_SWITCH ) { // ignore to prevent loop from pushing state from faceplate interaction + if (source != SRC_SWITCH && TuyaSerial) { // ignore to prevent loop from pushing state from faceplate interaction snprintf_P(log_data, sizeof(log_data), PSTR("TYA: SetDevicePower.rpower=%d"), rpower); AddLog(LOG_LEVEL_DEBUG); @@ -66,7 +66,7 @@ boolean TuyaSetPower() void LightSerialDuty(uint8_t duty) { - if (duty > 0 && !tuya_ignore_dim ) { + if (duty > 0 && !tuya_ignore_dim && TuyaSerial) { if (duty < 25) { duty = 25; // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself } @@ -149,7 +149,7 @@ void TuyaPacketProcess() void TuyaSerialInput() { - while (TuyaSerial->available()) { + while (TuyaSerial && TuyaSerial->available()) { yield(); serial_in_byte = TuyaSerial->read(); @@ -220,14 +220,16 @@ void TuyaInit() Settings.param[P_TUYA_DIMMER_ID] = TUYA_DIMMER_ID; } if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { - pin[GPIO_TUYA_RX] = pin[GPIO_RXD]; - pin[GPIO_TUYA_TX] = pin[GPIO_TXD]; + pin[GPIO_TUYA_RX] = 1; + pin[GPIO_TUYA_TX] = 3; } + snprintf_P(log_data, sizeof(log_data), "TYA: pin[GPIO_TUYA_RX] = %d, pin[GPIO_TUYA_TX] = %d", pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX]); + AddLog(LOG_LEVEL_DEBUG); TuyaSerial = new TasmotaSerial(pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX], 1); if (TuyaSerial->begin(baudrate)) { if (TuyaSerial->hardwareSerial()) { ClaimSerial(); - Serial.setDebugOutput(false); + //Serial.setDebugOutput(false); } } @@ -235,14 +237,16 @@ void TuyaInit() snprintf_P(log_data, sizeof(log_data), "TYA: Request MCU state"); AddLog(LOG_LEVEL_DEBUG); - TuyaSerial->write((uint8_t)0x55); // header 55AA - TuyaSerial->write((uint8_t)0xAA); - TuyaSerial->write((uint8_t)0x00); // version 00 - TuyaSerial->write((uint8_t)0x08); // command 08 - get status - TuyaSerial->write((uint8_t)0x00); - TuyaSerial->write((uint8_t)0x00); // following data length 0x00 - TuyaSerial->write((uint8_t)0x07); // checksum:sum of all bytes in packet mod 256 - TuyaSerial->flush(); + if(TuyaSerial){ + TuyaSerial->write((uint8_t)0x55); // header 55AA + TuyaSerial->write((uint8_t)0xAA); + TuyaSerial->write((uint8_t)0x00); // version 00 + TuyaSerial->write((uint8_t)0x08); // command 08 - get status + TuyaSerial->write((uint8_t)0x00); + TuyaSerial->write((uint8_t)0x00); // following data length 0x00 + TuyaSerial->write((uint8_t)0x07); // checksum:sum of all bytes in packet mod 256 + TuyaSerial->flush(); + } } boolean TuyaButtonPressed() From b605e5832083cd10e42bccfee7efbc764073dd7c Mon Sep 17 00:00:00 2001 From: Joel Stein Date: Sat, 27 Oct 2018 04:37:50 +0200 Subject: [PATCH 07/16] tuya: don't allow selecting GPIO08 --- sonoff/sonoff_template.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index a071dd8c7..f3712672f 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -1155,14 +1155,12 @@ const mytmplt kModules[MAXMODULE] PROGMEM = { { "Tuya Dimmer", // Tuya Dimmer (ESP8266 w/ separate MCU dimmer) // https://www.amazon.com/gp/product/B07CTNSZZ8/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1 GPIO_KEY1, // Virtual Button (controlled by MCU) - GPIO_USER, // GPIO01 MCU serial control + GPIO_USER, // GPIO01 MCU serial control GPIO_USER, - GPIO_USER, // GPIO03 MCU serial control + GPIO_USER, // GPIO03 MCU serial control GPIO_USER, GPIO_USER, - 0, 0, - GPIO_USER, - 0, 0, 0, // Flash connection + 0, 0, 0, 0, 0, 0, // Flash connection GPIO_USER, GPIO_USER, GPIO_LED1, // GPIO14 Green Led From ee2bed3b9a30072f55a50b65e6b7a8a032bea3ca Mon Sep 17 00:00:00 2001 From: Joel Stein Date: Sat, 27 Oct 2018 04:38:18 +0200 Subject: [PATCH 08/16] tuya: cleanup --- sonoff/xdrv_16_tuyadimmer.ino | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sonoff/xdrv_16_tuyadimmer.ino b/sonoff/xdrv_16_tuyadimmer.ino index e810aa26d..5b5a64062 100644 --- a/sonoff/xdrv_16_tuyadimmer.ino +++ b/sonoff/xdrv_16_tuyadimmer.ino @@ -219,12 +219,10 @@ void TuyaInit() if (!Settings.param[P_TUYA_DIMMER_ID]) { Settings.param[P_TUYA_DIMMER_ID] = TUYA_DIMMER_ID; } - if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { + if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { // fallback to hardware-serial if not explicitly selected pin[GPIO_TUYA_RX] = 1; pin[GPIO_TUYA_TX] = 3; } - snprintf_P(log_data, sizeof(log_data), "TYA: pin[GPIO_TUYA_RX] = %d, pin[GPIO_TUYA_TX] = %d", pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX]); - AddLog(LOG_LEVEL_DEBUG); TuyaSerial = new TasmotaSerial(pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX], 1); if (TuyaSerial->begin(baudrate)) { if (TuyaSerial->hardwareSerial()) { From 47a39a61ed944e1ee978641cc7633c2928503a9e Mon Sep 17 00:00:00 2001 From: Norbert Richter Date: Sat, 27 Oct 2018 10:37:33 +0200 Subject: [PATCH 09/16] decode-config.py v2.0.0000: add full backup/restore capability - add full backup/restore capability - add Tasmota .dmp format to be able to upload previously save Tasmota files - add help files decode-config.md - add Tasmota *.dmp format for backup - new args: --restore-file, --extension, --no-extension, --full-help, --verbose - changed args: --exit-on-error-only to --ignore-warnings --output-file to --backup-file --output-file-format to --backup-type --hide-pw to --json-hide-pw --unhide-pw to --json-unhide-pw - removed (hidden) args: --sort, --unsort, --raw-values, --no-raw-values -raw-keys, -no-raw-keys defaults are no raw keys/values and sort --- tools/decode-config.html | 230 +++++ tools/decode-config.md | 253 +++++ tools/decode-config.py | 1964 ++++++++++++++++++++++++++++---------- 3 files changed, 1920 insertions(+), 527 deletions(-) create mode 100644 tools/decode-config.html create mode 100644 tools/decode-config.md mode change 100644 => 100755 tools/decode-config.py diff --git a/tools/decode-config.html b/tools/decode-config.html new file mode 100644 index 000000000..dd3d43d01 --- /dev/null +++ b/tools/decode-config.html @@ -0,0 +1,230 @@ +

decode-config.py

+

decode-config.py backup and restore Sonoff-Tasmota configuration.

+

Comparing backup files created by decode-config.py and *.dmp files created by Tasmota "Backup/Restore Configuration":

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 decode-config.py
*.json file
Sonoff-Tasmota
*.dmp file
EncryptedNoYes
ReadableYesNo
Simply editableYesNo
Simply batch processingYesNo
+

decode-config.py handles Tasmota configurations for release version since 5.10.0 up to now.

+

Content

+ +

Prerequisite

+
    +
  • Python)
    This program is written in Python) so you need to install a python environment (for details see Python Setup and Usage)
  • +
  • Sonoff-Tasmota Firmware with enabled Web-Server
    To backup or restore configurations from/to a Sonoff-Tasmota device you need a firmare with enabled web-server in admin mode (command WebServer 2). +
    Only self compiled firmware may do not have a web-server sod if you use your own compiled firmware be aware to enable the web-server, otherwise you can only use the --file parameter as source.
  • +
+

File Types

+

decode-config.py can handle the following backup file types:

+

.dmp Format

+

Configuration data as used by Tasmota "Backup/Restore Configuration" web interface.
This format is binary and encrypted.

+

.json Format

+

Configuration data in JSON-format.
This format is decrypted, human readable and editable and can also be used for the --restore-file command.
This file will becreated by decode-config.py using --backup-file with --backup-type json parameter (default).

+

.bin Format

+

Configuration data in binary format.
This format is binary decryptet, editable (e.g. using a hex editor) and can also be used for --restore-file command.
It will be created by decode-config.py using --backup-file with --backup-type bin.
Note:
This file is 4 byte longer than an original .dmp file due to an prefix header at the beginning. The file data starting at address position 4 are containing the same as the struct SYSCFG from Tasmota settings.h in decrypted format.

+

File extensions

+

decode-config.py uses auto extension as default for backup filenames; you don't need to append extensions to your backup file, it will be selected based on --backup-type argument.
If you want using your own extension use the --no-extension argument.

+

Usage

+

After download don't forget to set exec flag under linux with chmod +x decode-config.py or call the program using python decode-config.py....

+

Basics

+

At least pass a source where you want to read the configuration data from using -f <filename> or -d <host>:

+

The source can be either

+
    +
  • a Tasmota device hostname or IP by passing it using the -d <host> arg
  • +
  • or a previously stored Tasmota *.dmpconfiguration file by passing the filename using-f ` arg
  • +
+

Example:

+
decode-config.py -d sonoff-4281
+

will output a human readable configuration in JSON-format:

+
{
+  "altitude": 112, 
+  "baudrate": 115200, 
+  "blinkcount": 10, 
+  "blinktime": 10, 
+...
+  "ws_width": [
+    1, 
+    3, 
+    5
+  ]
+}
+

Save backup file

+

To save the output as backup file --backup-file <filename>, you can use placeholder for Version, Friendlyname and Hostname:

+
decode-config.py -d sonoff-4281 --backup-file Config_@f_@v
+

If you have setup a WebPassword within Tasmota, use

+
decode-config.py -d sonoff-4281 -p <yourpassword> --backup-file Config_@f_@v
+

will create a file like Config_Sonoff_x.x.x.json. Because it is in JSON format, you can read and edit the file with any raw text editor.

+

Restore backup file

+

Reading back a saved (and possible changed) backup file use the --restore-file <filename> arg. This will read the (changed) configuration data from this file and send it back to the source device or filename.

+

To restore the previously save backup file Config_Sonoff_6.2.1.json to device sonoff-4281 use:

+
decode-config.py -d sonoff-4281 --restore-file Config_Sonoff_6.2.1.json
+

with password set by WebPassword:

+
decode-config.py -d sonoff-4281 -p <yourpassword> --restore-file Config_Sonoff_6.2.1.json
+

Configuration file

+

Each argument that start with -- (eg. --file) can also be set in a config file (specified via -c). Config file syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at https://pypi.org/project/ConfigArgParse).

+

If an argument is specified in more than one place, then commandline values override config file values which override defaults. This is usefull if you always use the same argument or a basic set of arguments.

+

The http authentication credentials --username and --password is predestinated to store it in a file instead using it on your command line as argument:

+

e.g. my.conf:

+
[source]
+username = admin
+password = myPaszxwo!z
+

To make a backup file from example above you can now pass the config file instead using the password on command line:

+
decode-config.py -d sonoff-4281 -c my.conf --backup-file Config_@f_@v
+

More program arguments

+

For better reading your porgram arguments each short written arg (minus sign -) has a corresponding readable long version (two minus signs --), eg. --device for -d or --file for -f (note: not even all -- arg has a corresponding - one).

+

A short list of possible program args is displayed using -h or --help.

+

For advanced help use -H or --full-help:

+
usage: decode-config.py [-f <filename>] [-d <host>] [-P <port>]
+                        [-u <username>] [-p <password>] [-i <filename>]
+                        [-o <filename>] [-F json|bin|dmp] [-E] [-e]
+                        [--json-indent <indent>] [--json-compact]
+                        [--json-hide-pw] [--json-unhide-pw] [-h] [-H] [-v]
+                        [-V] [-c <filename>] [--ignore-warnings]
+
+Backup/Restore Sonoff-Tasmota configuration data. Args that start with '--'
+(eg. -f) can also be set in a config file (specified via -c). Config file
+syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at
+https://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:
+  -c, --config <filename>
+                        program config file - can be used to set default
+                        command args (default: None)
+  --ignore-warnings     do not exit on warnings. Not recommended, used by your
+                        own responsibility!
+
+Source:
+  Read/Write Tasmota configuration from/to
+
+  -f, --file, --tasmota-file <filename>
+                        file to retrieve/write Tasmota configuration from/to
+                        (default: None)'
+  -d, --device, --host <host>
+                        hostname or IP address to retrieve/send Tasmota
+                        configuration from/to (default: None)
+  -P, --port <port>     TCP/IP port number to use for the host connection
+                        (default: 80)
+  -u, --username <username>
+                        host HTTP access username (default: admin)
+  -p, --password <password>
+                        host HTTP access password (default: None)
+
+Backup/Restore:
+  Backup/Restore configuration file specification
+
+  -i, --restore-file <filename>
+                        file to restore configuration from (default: None).
+                        Replacements: @v=firmware version, @f=device friendly
+                        name, @h=device hostname
+  -o, --backup-file <filename>
+                        file to backup configuration to (default: None).
+                        Replacements: @v=firmware version, @f=device friendly
+                        name, @h=device hostname
+  -F, --backup-type json|bin|dmp
+                        backup filetype (default: 'json')
+  -E, --extension       append filetype extension for -i and -o filename
+                        (default)
+  -e, --no-extension    do not append filetype extension, use -i and -o
+                        filename as passed
+
+JSON:
+  JSON backup format specification
+
+  --json-indent <indent>
+                        pretty-printed JSON output using indent level
+                        (default: 'None'). -1 disables indent.
+  --json-compact        compact JSON output by eliminate whitespace
+  --json-hide-pw        hide passwords (default)
+  --json-unhide-pw      unhide passwords
+
+Info:
+  additional information
+
+  -h, --help            show usage help message and exit
+  -H, --full-help       show full help message and exit
+  -v, --verbose         produce more output about what the program does
+  -V, --version         show program's version number and exit
+
+Either argument -d <host> or -f <filename> must be given.
+

Examples

+

The most of the examples are for linux command line. Under Windows call the program using python decode-config.py ....

+

Config file

+

Note: The example contains .ini style sections [...]. Sections are always treated as comment and serves as clarity only. +For further details of config file syntax see https://pypi.org/project/ConfigArgParse.

+

my.conf

+
[Source]
+username = admin
+password = myPaszxwo!z
+
+[JSON]
+json-indent 2
+

Using Tasmota binary configuration files

+
    +
  1. Restore a Tasmota configuration file

    +

    decode-config.py -c my.conf -d sonoff --restore-file Config_Sonoff_6.2.1.dmp

    +
  2. +
  3. Backup device using Tasmota configuration compatible format

    +

    a) use file extension to choice the file format

    +

    decode-config.py -c my.conf -d sonoff --backup-file Config_@f_@v.dmp

    +

    b) use args to choice the file format

    +

    decode-config.py -c my.conf -d sonoff --backup-type dmp --backup-file Config_@f_@v

    +
  4. +
+

Use batch processing

+
for device in sonoff1 sonoff2 sonoff3; do ./decode-config.py -c my.conf -d $device -o Config_@f_@v
+

or under windows

+
for device in (sonoff1 sonoff2 sonoff3) do python decode-config.py -c my.conf -d %device -o Config_@f_@v
+

will produce JSON configuration files for host sonoff1, sonoff2 and sonoff3 using friendly name and Tasmota firmware version for backup filenames.

diff --git a/tools/decode-config.md b/tools/decode-config.md new file mode 100644 index 000000000..d1cb05bd8 --- /dev/null +++ b/tools/decode-config.md @@ -0,0 +1,253 @@ +# decode-config.py +_decode-config.py_ backup and restore Sonoff-Tasmota configuration. + +Comparing backup files created by *decode-config.py* and *.dmp files created by Tasmota "Backup/Restore Configuration": + +|   | decode-config.py
*.json file | Sonoff-Tasmota
*.dmp file | +|-------------------------|:-------------------------------:|:-----------------------------------:| +| Encrypted | No | Yes | +| Readable | Yes | No | +| Simply editable | Yes | No | +| Simply batch processing | Yes | No | + +_decode-config.py_ handles Tasmota configurations for release version since 5.10.0 up to now. + +# Content +* [Prerequisite](decode-config.md#prerequisite) +* [File Types](decode-config.md#file-types) + * [.dmp File Format](decode-config.md#-dmp-file-format) + * [.json File Format](decode-config.md#-json-file-format) + * [.bin File Format](decode-config.md#-bin-file-format) + * [File extensions](decode-config.md#file-extensions) +* [Usage](decode-config.md#usage) + * [Basics](decode-config.md#basics) + * [Save backup file](decode-config.md#save-backup-file) + * [Restore backup file](decode-config.md#restore-backup-file) + * [Configuration file](decode-config.md#configuration-file) + * [More program arguments](decode-config.md#more-program-arguments) + * [Examples](decode-config.md#examples) + * [Config file](decode-config.md#config-file) + * [Using Tasmota binary configuration files](decode-config.md#using-tasmota-binary-configuration-files) + * [Use batch processing](decode-config.md#use-batch-processing) + +## Prerequisite +* [Python](https://en.wikipedia.org/wiki/Python_(programming_language)) + This program is written in [Python](https://en.wikipedia.org/wiki/Python_(programming_language)) so you need to install a python environment (for details see [Python Setup and Usage](https://docs.python.org/2.7/using/index.html)) +* [Sonoff-Tasmota](https://github.com/arendst/Sonoff-Tasmota) [Firmware](https://github.com/arendst/Sonoff-Tasmota/releases) with enabled Web-Server + To backup or restore configurations from/to a Sonoff-Tasmota device you need a firmare with enabled web-server in admin mode (command [WebServer 2](https://github.com/arendst/Sonoff-Tasmota/wiki/Commands#wifi)). +
Only self compiled firmware may do not have a web-server sod if you use your own compiled firmware be aware to enable the web-server, otherwise you can only use the `--file` parameter as source. + +## File Types +_decode-config.py_ can handle the following backup file types: +### .dmp Format +Configuration data as used by Tasmota "Backup/Restore Configuration" web interface. +This format is binary and encrypted. +### .json Format +Configuration data in [JSON](http://www.json.org/)-format. +This format is decrypted, human readable and editable and can also be used for the `--restore-file` command. +This file will becreated by _decode-config.py_ using `--backup-file` with `--backup-type json` parameter (default). +### .bin Format +Configuration data in binary format. +This format is binary decryptet, editable (e.g. using a hex editor) and can also be used for `--restore-file` command. +It will be created by _decode-config.py_ using `--backup-file` with `--backup-type bin`. +Note: +This file is 4 byte longer than an original .dmp file due to an prefix header at the beginning. The file data starting at address position 4 are containing the same as the **struct SYSCFG** from Tasmota [settings.h](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/settings.h) in decrypted format. + +#### File extensions +_decode-config.py_ uses auto extension as default for backup filenames; you don't need to append extensions to your backup file, it will be selected based on `--backup-type` argument. +If you want using your own extension use the `--no-extension` argument. + +## Usage +After download don't forget to set exec flag under linux with `chmod +x decode-config.py` or call the program using `python decode-config.py...`. + +### Basics +At least pass a source where you want to read the configuration data from using `-f ` or `-d `: + +The source can be either +* a Tasmota device hostname or IP by passing it using the `-d ` arg +* or a previously stored Tasmota *.dmp` configuration file by passing the filename using `-f ` arg + +Example: + + decode-config.py -d sonoff-4281 + +will output a human readable configuration in [JSON](http://www.json.org/)-format: + + { + "altitude": 112, + "baudrate": 115200, + "blinkcount": 10, + "blinktime": 10, + ... + "ws_width": [ + 1, + 3, + 5 + ] + } + + +### Save backup file +To save the output as backup file `--backup-file `, you can use placeholder for Version, Friendlyname and Hostname: + + decode-config.py -d sonoff-4281 --backup-file Config_@f_@v + +If you have setup a WebPassword within Tasmota, use + + decode-config.py -d sonoff-4281 -p --backup-file Config_@f_@v + +will create a file like `Config_Sonoff_x.x.x.json`. Because it is in JSON format, you can read and edit the file with any raw text editor. + +### Restore backup file +Reading back a saved (and possible changed) backup file use the `--restore-file ` arg. This will read the (changed) configuration data from this file and send it back to the source device or filename. + +To restore the previously save backup file `Config_Sonoff_6.2.1.json` to device `sonoff-4281` use: + + decode-config.py -d sonoff-4281 --restore-file Config_Sonoff_6.2.1.json + +with password set by WebPassword: + + decode-config.py -d sonoff-4281 -p --restore-file Config_Sonoff_6.2.1.json + +### Configuration file +Each argument that start with `--` (eg. `--file`) can also be set in a config file (specified via -c). Config file syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at [https://pypi.org/project/ConfigArgParse](https://pypi.org/project/ConfigArgParse/)). + +If an argument is specified in more than one place, then commandline values override config file values which override defaults. This is usefull if you always use the same argument or a basic set of arguments. + +The http authentication credentials `--username` and `--password` is predestinated to store it in a file instead using it on your command line as argument: + +e.g. my.conf: + + [source] + username = admin + password = myPaszxwo!z + +To make a backup file from example above you can now pass the config file instead using the password on command line: + + decode-config.py -d sonoff-4281 -c my.conf --backup-file Config_@f_@v + + + +### More program arguments +For better reading your porgram arguments each short written arg (minus sign `-`) has a corresponding readable long version (two minus signs `--`), eg. `--device` for `-d` or `--file` for `-f` (note: not even all `--` arg has a corresponding `-` one). + +A short list of possible program args is displayed using `-h` or `--help`. + +For advanced help use `-H` or `--full-help`: + + usage: decode-config.py [-f ] [-d ] [-P ] + [-u ] [-p ] [-i ] + [-o ] [-F json|bin|dmp] [-E] [-e] + [--json-indent ] [--json-compact] + [--json-hide-pw] [--json-unhide-pw] [-h] [-H] [-v] + [-V] [-c ] [--ignore-warnings] + + Backup/Restore Sonoff-Tasmota configuration data. Args that start with '--' + (eg. -f) can also be set in a config file (specified via -c). Config file + syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at + https://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: + -c, --config + program config file - can be used to set default + command args (default: None) + --ignore-warnings do not exit on warnings. Not recommended, used by your + own responsibility! + + Source: + Read/Write Tasmota configuration from/to + + -f, --file, --tasmota-file + file to retrieve/write Tasmota configuration from/to + (default: None)' + -d, --device, --host + hostname or IP address to retrieve/send Tasmota + configuration from/to (default: None) + -P, --port TCP/IP port number to use for the host connection + (default: 80) + -u, --username + host HTTP access username (default: admin) + -p, --password + host HTTP access password (default: None) + + Backup/Restore: + Backup/Restore configuration file specification + + -i, --restore-file + file to restore configuration from (default: None). + Replacements: @v=firmware version, @f=device friendly + name, @h=device hostname + -o, --backup-file + file to backup configuration to (default: None). + Replacements: @v=firmware version, @f=device friendly + name, @h=device hostname + -F, --backup-type json|bin|dmp + backup filetype (default: 'json') + -E, --extension append filetype extension for -i and -o filename + (default) + -e, --no-extension do not append filetype extension, use -i and -o + filename as passed + + JSON: + JSON backup format specification + + --json-indent + pretty-printed JSON output using indent level + (default: 'None'). -1 disables indent. + --json-compact compact JSON output by eliminate whitespace + --json-hide-pw hide passwords (default) + --json-unhide-pw unhide passwords + + Info: + additional information + + -h, --help show usage help message and exit + -H, --full-help show full help message and exit + -v, --verbose produce more output about what the program does + -V, --version show program's version number and exit + + Either argument -d or -f must be given. + + +### Examples +The most of the examples are for linux command line. Under Windows call the program using `python decode-config.py ...`. + +#### Config file +Note: The example contains .ini style sections `[...]`. Sections are always treated as comment and serves as clarity only. +For further details of config file syntax see [https://pypi.org/project/ConfigArgParse](https://pypi.org/project/ConfigArgParse/). + +*my.conf* + + [Source] + username = admin + password = myPaszxwo!z + + [JSON] + json-indent 2 + +#### Using Tasmota binary configuration files + +1. Restore a Tasmota configuration file + + `decode-config.py -c my.conf -d sonoff --restore-file Config_Sonoff_6.2.1.dmp` + +2. Backup device using Tasmota configuration compatible format + + a) use file extension to choice the file format + + `decode-config.py -c my.conf -d sonoff --backup-file Config_@f_@v.dmp` + + b) use args to choice the file format + + `decode-config.py -c my.conf -d sonoff --backup-type dmp --backup-file Config_@f_@v` + +#### Use batch processing + + for device in sonoff1 sonoff2 sonoff3; do ./decode-config.py -c my.conf -d $device -o Config_@f_@v + +or under windows + + for device in (sonoff1 sonoff2 sonoff3) do python decode-config.py -c my.conf -d %device -o Config_@f_@v + +will produce JSON configuration files for host sonoff1, sonoff2 and sonoff3 using friendly name and Tasmota firmware version for backup filenames. diff --git a/tools/decode-config.py b/tools/decode-config.py old mode 100644 new mode 100755 index ffa78c031..1eb991d11 --- a/tools/decode-config.py +++ b/tools/decode-config.py @@ -1,10 +1,9 @@ #!/usr/bin/env python -#!/usr/bin/env python # -*- coding: utf-8 -*- -VER = '1.5.0013' +VER = '2.0.0000' """ - decode-config.py - Decode configuration of Sonoff-Tasmota device + decode-config.py - Backup/Restore Sonoff-Tasmota configuration data Copyright (C) 2018 Norbert Richter @@ -29,68 +28,83 @@ Requirements: Instructions: 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 + or use -f to read a configuration file saved using Tasmota Web-UI + + For further information read 'decode-config.md' - For help execute command with argument -h + For help execute command with argument -h (or -H for advanced help) -Usage: - decode-config.py [-h] [-f ] [-d ] [-u ] - [-p ] [--json-indent ] - [--json-compact] [--sort] [--unsort] [--raw-values] - [--no-raw-values] [--raw-keys] [--no-raw-keys] - [--hide-pw] [--unhide-pw] [-o ] - [--output-file-format ] [-c ] - [--exit-on-error-only] [-V] +Usage: decode-config.py [-f ] [-d ] [-P ] + [-u ] [-p ] [-i ] + [-o ] [-F json|bin|dmp] [-E] [-e] + [--json-indent ] [--json-compact] + [--json-hide-pw] [--json-unhide-pw] [-h] [-H] [-v] + [-V] [-c ] [--ignore-warnings] - 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 + Backup/Restore Sonoff-Tasmota configuration data. Args that start with '--' + (eg. -f) can also be set in a config file (specified via -c). Config file + syntax allows: key=value, flag=true, stuff=[a,b,c] (for details, see syntax at https://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 , --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! + -c, --config + program config file - can be used to set default + command args (default: None) + --ignore-warnings do not exit on warnings. Not recommended, used by your + own responsibility! - source: - -f , --file - file to retrieve Tasmota configuration from (default: - None)' - -d , --device - hostname or IP address to retrieve Tasmota - configuration from (default: None) - -u , --username + Source: + Read/Write Tasmota configuration from/to + + -f, --file, --tasmota-file + file to retrieve/write Tasmota configuration from/to + (default: None)' + -d, --device, --host + hostname or IP address to retrieve/send Tasmota + configuration from/to (default: None) + -P, --port TCP/IP port number to use for the host connection + (default: 80) + -u, --username host HTTP access username (default: admin) - -p , --password + -p, --password host HTTP access password (default: None) - config: - --json-indent - pretty-printed JSON output using indent level - (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 - -o , --output-file - file to store configuration to (default: None). - Replacements: @v=Tasmota version, @f=friendly name - --output-file-format - output format ('json' or 'binary', default: 'json') + Backup/Restore: + Backup/Restore configuration file specification - info: + -i, --restore-file + file to restore configuration from (default: None). + Replacements: @v=firmware version, @f=device friendly + name, @h=device hostname + -o, --backup-file + file to backup configuration to (default: None). + Replacements: @v=firmware version, @f=device friendly + name, @h=device hostname + -F, --backup-type json|bin|dmp + backup filetype (default: 'json') + -E, --extension append filetype extension for -i and -o filename + (default) + -e, --no-extension do not append filetype extension, use -i and -o + filename as passed + + JSON: + JSON backup format specification + + --json-indent + pretty-printed JSON output using indent level + (default: 'None'). -1 disables indent. + --json-compact compact JSON output by eliminate whitespace + --json-hide-pw hide passwords (default) + --json-unhide-pw unhide passwords + + Info: + additional information + + -h, --help show usage help message and exit + -H, --full-help show full help message and exit + -v, --verbose produce more output about what the program does -V, --version show program's version number and exit Either argument -d or -f must be given. @@ -98,29 +112,54 @@ Usage: 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 - 4xx, 5xx: HTTP error + 1: restore skipped + 2: program argument error + 3: file not found + 4: data size mismatch + 5: data CRC error + 6: unsupported configuration version + 7: configuration file read error + 8: JSON file decoding error + 9: Restore file data error + 10: Device data download error + 11: Device data upload error + 20: python module missing + 21: Internal error + >21: python library exit code + 4xx, 5xx: HTTP errors """ +class ExitCode: + OK = 0 + RESTORE_SKIPPED = 1 + ARGUMENT_ERROR = 2 + FILE_NOT_FOUND = 3 + DATA_SIZE_MISMATCH = 4 + DATA_CRC_ERROR = 5 + UNSUPPORTED_VERSION = 6 + FILE_READ_ERROR = 7 + JSON_READ_ERROR = 8 + RESTORE_DATA_ERROR = 9 + DOWNLOAD_CONFIG_ERROR = 10 + UPLOAD_CONFIG_ERROR = 11 + MODULE_NOT_FOUND = 20 + INTERNAL_ERROR = 21 + import os.path import io -import sys +import sys, platform def ModuleImportError(module): er = str(module) - print("{}. Try 'pip install {}' to install it".format(er,er.split(' ')[len(er.split(' '))-1]) ) - sys.exit(9) + print >> sys.stderr, "{}. Try 'pip install {}' to install it".format(er,er.split(' ')[len(er.split(' '))-1]) + sys.exit(ExitCode.MODULE_NOT_FOUND) try: + from datetime import datetime import struct + import socket import re import math - from datetime import datetime + import inspect import json import configargparse import pycurl @@ -129,37 +168,43 @@ except ImportError, e: ModuleImportError(e) -PROG='{} v{} by Norbert Richter'.format(os.path.basename(sys.argv[0]),VER) +PROG='{} v{} by Norbert Richter '.format(os.path.basename(sys.argv[0]),VER) -CONFIG_FILE_XOR = 0x5A -BINARYFILE_MAGIC = 0x63576223 - -args = {} -DEFAULTS = { +CONFIG_FILE_XOR = 0x5A +BINARYFILE_MAGIC = 0x63576223 +STR_ENCODING = 'utf8' +DEFAULTS = { 'DEFAULT': { 'configfile': None, - 'exitonwarning':True, + 'ignorewarning':False, }, 'source': { 'device': None, + 'port': 80, 'username': 'admin', 'password': None, 'tasmotafile': None, }, - 'config': + 'backup': + { + 'restorefile': None, + 'backupfile': None, + 'backupfileformat': 'json', + 'extension': True, + }, + 'jsonformat': { 'jsonindent': None, 'jsoncompact': False, - 'sort': True, - 'rawvalues': False, - 'rawkeys': True, - 'hidepw': True, - 'outputfile': None, - 'outputfileformat': 'json', + 'jsonsort': True, + 'jsonrawvalues':False, + 'jsonrawkeys': False, + 'jsonhidepw': True, }, } +args = {} exitcode = 0 @@ -218,33 +263,47 @@ Settings dictionary describes the config file fields definition: [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. + converter (optional) + Conversion methode(s): ()|'xxx'|func + Read conversion is used if args.jsonrawvalues is False + Write conversion is used if jsonrawvalues from restore json + file is False or args.jsonrawvalues is False. + Converter is either a single methode 'xxx'|func or a tuple + Single methode will be used for reading conversion only: + 'xxx': + string will used for reading conversion and will be + evaluate as is, this can also contain python code. + Use '$' for current value. func: - a function defines the name of a formating function + name of a formating function that will be used for + reading conversion + None: + will read as definied in + (read, write): + a tuple with 2 objects. Each can be of the same type + as the single method above ('xxx'|func) or None. + read: + method will be used for read conversion + (unpack data from dmp object) + write: + method will be used for write conversion + (pack data to dmp object) + If write method is None indicates value is + readable only and will not be write """ -# 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 +def passwordread(value): + return "********" if args.jsonhidepw else value +def passwordwrite(value): + return None if value=="********" else value Setting_5_10_0 = { - 'cfg_holder': (' version number from read binary data to search for + + @return: + template sizes as list [] + """ + sizes = [] + for cfg in Settings: + sizes.append(cfg[1]) + # return unique sizes only (remove duplicates) + return list(set(sizes)) + + +def GetTemplateSetting(decode_cfg): + """ + Search for version, template, size and settings to be used depending on given binary config data + + @param decode_cfg: + binary config data (decrypted) + + @return: + version, template, size, settings to use; None if version is invalid + """ + try: + version = GetField(decode_cfg, 'version', Setting_6_2_1['version'], raw=True) + except: + return None,None,None,None + + # search setting definition + template = None + setting = None + size = None + for cfg in Settings: + if version >= cfg[0]: + template = cfg + size = template[1] + setting = template[2] + break + + return version, template, size, setting + + +class LogType: 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) +def message(msg, typ=None, status=None, line=None): + """ + Writes a message to stdout + + @param msg: + message to output + @param typ: + INFO, WARNING or ERROR + @param status: + status number + """ + print >> sys.stderr, '{styp}{sdelimiter}{sstatus}{slineno}{scolon}{smgs}'.format(\ + styp=typ if typ is not None else '', + sdelimiter=' ' if status is not None and status>0 and typ is not None else '', + sstatus=status if status is not None and status>0 else '', + scolon=': ' if typ is not None or line is not None else '', + smgs=msg, + slineno=' (@{:04d})'.format(line) if line is not None else '') -def exit(status=0, message="end", typ='ERROR', doexit=True): +def exit(status=0, msg="end", typ=LogType.ERROR, src=None, doexit=True, line=None): """ Called when the program should be exit @param status: the exit status program returns to callert - @param message: - the message logged before exit + @param msg: + the msg logged before exit @param typ: - message type: 'INFO', 'WARNING' or 'ERROR' + msg type: 'INFO', 'WARNING' or 'ERROR' @param doexit: True to exit program, otherwise return """ - logger = Log() - logger.message(message, typ=typ if status!=0 else 'INFO', status=status, jsonformat=True ) + if src is not None: + msg = '{} ({})'.format(src, msg) + message(msg, typ=typ if status!=ExitCode.OK else LogType.INFO, status=status, line=line) exitcode = status if doexit: sys.exit(exitcode) +def ShortHelp(doexit=True): + """ + Show short help (usage) only - ued by own -h handling + + @param doexit: + sys.exit with OK if True + """ + print parser.description + print + parser.print_usage() + print + print "For advanced help use '{prog} -H' or '{prog} --full-help'".format(prog=os.path.basename(sys.argv[0])) + if doexit: + sys.exit(ExitCode.OK) + + +class HTTPHeader: + """ + pycurl helper class retrieving the request header + """ + def __init__(self): + self.contents = '' + + def clear(self): + self.contents = '' + + def store(self, _buffer): + self.contents = "{}{}".format(self.contents, _buffer) + + def response(self): + header = str(self.contents).split('\n') + if len(header)>0: + return header[0].rstrip() + return '' + + def contenttype(self): + for item in str(self.contents).split('\n'): + ditem = item.split(":") + if ditem[0].strip().lower()=='content-type' and len(ditem)>1: + return ditem[1].strip() + return '' + + def __str__(self): + return self.contents + + +class CustomHelpFormatter(configargparse.HelpFormatter): + """ + Class for customizing the help output + """ + + def _format_action_invocation(self, action): + """ + Reformat multiple metavar output + -d , --device , --host + to single output + -d, --device, --host + """ + + orgstr = configargparse.HelpFormatter._format_action_invocation(self, action) + if orgstr and orgstr[0] != '-': # only optional arguments + return orgstr + res = getattr(action, '_formatted_action_invocation', None) + if res: + return res + + options = orgstr.split(', ') + if len(options) <=1: + action._formatted_action_invocation = orgstr + return orgstr + + return_list = [] + for option in options: + meta = "" + arg = option.split(' ') + if len(arg)>1: + meta = arg[1] + return_list.append(arg[0]) + if len(meta) >0 and len(return_list) >0: + return_list[len(return_list)-1] += " "+meta + action._formatted_action_invocation = ', '.join(return_list) + return action._formatted_action_invocation + + # ---------------------------------------------------------------------- # Tasmota config data handling # ---------------------------------------------------------------------- -def GetFilenameReplaced(filename, configuration): +class FileType: + FILE_NOT_FOUND = None + DMP = 'dmp' + JSON = 'json' + BIN = 'bin' + UNKNOWN = 'unknown' + INCOMPLETE_JSON = 'incomplete json' + INVALID_JSON = 'invalid json' + INVALID_BIN = 'invalid bin' + + +def GetFileType(filename): + """ + Get the FileType class member of a given filename + + @param filename: + filename of the file to analyse + + @return: + FileType class member + """ + filetype = FileType.UNKNOWN + + # try filename + try: + isfile = os.path.isfile(filename) + try: + f = open(filename, "r") + try: + # try reading as json + inputjson = json.load(f) + if 'header' in inputjson: + filetype = FileType.JSON + else: + filetype = FileType.INCOMPLETE_JSON + except ValueError: + filetype = FileType.INVALID_JSON + # not a valid json, get filesize and compare it with all possible sizes + try: + size = os.path.getsize(filename) + except: + filetype = FileType.UNKNOWN + sizes = GetTemplateSizes() + + # size is one of a dmp file size + if size in sizes: + filetype = FileType.DMP + elif (size - ((len(hex(BINARYFILE_MAGIC))-2)/2)) in sizes: + # check if the binary file has the magic header + try: + inputfile = open(filename, "rb") + inputbin = inputfile.read() + inputfile.close() + if struct.unpack_from('>24) & 0xff) + minor = ((version>>16) & 0xff) + release = ((version>> 8) & 0xff) + subrelease = (version & 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 = '' + return "{:d}.{:d}.{:d}{}{}".format( major, minor, release, '.' if (major>=6 and subreleasestr!='') else '', subreleasestr) + + +def MakeValidFilename(filename): + """ + Make a valid filename + + @param filename: + filename src + + @return: + valid filename removed invalid chars and replace space with _ + """ + try: + filename = filename.decode('unicode-escape').translate(dict((ord(char), None) for char in '\/*?:"<>|')) + except: + pass + return str(filename.replace(' ','_')) + + +def MakeFilename(filename, filetype, decode_cfg): """ Replace variable within a filename @@ -863,35 +1171,223 @@ def GetFilenameReplaced(filename, configuration): @v: Tasmota version @f: - FriendlyName + friendlyname + @h: + hostname + @param filetype: + FileType.x object - creates extension if not None + @param decode_cfg: + binary config data (decrypted) - @return: New filename with replacements + @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) + if 'version' in decode_cfg: + v = GetVersionStr( int(str(decode_cfg['version']), 0) ) filename = filename.replace('@v', v) - if 'friendlyname' in configuration: - filename = filename.replace('@f', configuration['friendlyname'][0] ) + if 'friendlyname' in decode_cfg: + filename = filename.replace('@f', decode_cfg['friendlyname'][0] ) + if 'hostname' in decode_cfg: + filename = filename.replace('@h', decode_cfg['hostname'] ) + + filename = MakeValidFilename(filename) + ext = '' + try: + name, ext = os.path.splitext(filename) + except: + pass + if len(ext) and ext[0]=='.': + ext = ext[1:] + if filetype is not None and args.extension and (len(ext)<2 or all(c.isdigit() for c in ext)): + filename += '.'+filetype.lower() return filename +def MakeUrl(host, port=80, location=''): + """ + Create a Tasmota host url + + @param host: + hostname or IP of Tasmota host + @param port: + port number to use for http connection + @param location: + http url location + + @return: + Tasmota http url + """ + return "http://{shost}{sdelimiter}{sport}/{slocation}".format(\ + shost=host, + sdelimiter=':' if port != 80 else '', + sport=port if port != 80 else '', + slocation=location ) + + +def PullTasmotaConfig(): + """ + Pull config from Tasmota device/file + + @return: + binary config data (encrypted) or None on error + """ + + if args.device is not None: + # read config direct from device via http + + c = pycurl.Curl() + buffer = io.BytesIO() + c.setopt(c.WRITEDATA, buffer) + header = HTTPHeader() + c.setopt(c.HEADERFUNCTION, header.store) + c.setopt(c.FOLLOWLOCATION, True) + c.setopt(c.URL, MakeUrl(args.device, args.port, 'dl')) + 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.VERBOSE, False) + + responsecode = 200 + try: + c.perform() + responsecode = c.getinfo(c.RESPONSE_CODE) + response = header.response() + except Exception, e: + exit(e[0], e[1],line=inspect.getlineno(inspect.currentframe())) + finally: + c.close() + + if responsecode>=400: + exit(responsecode, 'HTTP result: {}'.format(header.response()),line=inspect.getlineno(inspect.currentframe())) + elif header.contenttype()!='application/octet-stream': + exit(ExitCode.DOWNLOAD_CONFIG_ERROR, "Device did not response properly, may be Tasmota webserver admin mode is disabled (WebServer 2)",line=inspect.getlineno(inspect.currentframe())) + encode_cfg = buffer.getvalue() + + elif args.tasmotafile is not None: + # read config from a file + if not os.path.isfile(args.tasmotafile): # check file exists + exit(ExitCode.FILE_NOT_FOUND, "File '{}' not found".format(args.tasmotafile),line=inspect.getlineno(inspect.currentframe())) + try: + tasmotafile = open(args.tasmotafile, "rb") + encode_cfg = tasmotafile.read() + tasmotafile.close() + except Exception, e: + exit(e[0], "'{}' {}".format(args.tasmotafile, e[1]),line=inspect.getlineno(inspect.currentframe())) + + else: + return None + + return encode_cfg + + +def PushTasmotaConfig(encode_cfg, host, port, username=DEFAULTS['source']['username'], password=None): + """ + Upload binary data to a Tasmota host using http + + @param encode_cfg: + encrypted binary data or filename containing Tasmota encrypted binary config + @param host: + hostname or IP of Tasmota device + @param username: + optional username for Tasmota web login + @param password + optional password for Tasmota web login + + @return + errorcode, errorstring + errorcode=0 if success, otherwise http response or exception code + """ + # ~ return 0, 'OK' + + if isinstance(encode_cfg, bytearray): + encode_cfg = str(encode_cfg) + + c = pycurl.Curl() + buffer = io.BytesIO() + c.setopt(c.WRITEDATA, buffer) + header = HTTPHeader() + c.setopt(c.HEADERFUNCTION, header.store) + c.setopt(c.FOLLOWLOCATION, True) + # get restore config page first to set internal Tasmota vars + c.setopt(c.URL, MakeUrl(host, port, 'rs?')) + 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.HTTPGET, True) + c.setopt(c.VERBOSE, False) + + responsecode = 200 + try: + c.perform() + responsecode = c.getinfo(c.RESPONSE_CODE) + except Exception, e: + c.close() + return e[0], e[1] + + if responsecode>=400: + c.close() + return responsecode, header.response() + elif header.contenttype()!='text/html': + c.close() + return ExitCode.UPLOAD_CONFIG_ERROR, "Device did not response properly, may be Tasmota webserver admin mode is disabled (WebServer 2)" + + # post data + header.clear() + c.setopt(c.HEADERFUNCTION, header.store) + c.setopt(c.POST, 1) + c.setopt(c.URL, MakeUrl(host, port, 'u2')) + try: + isfile = os.path.isfile(encode_cfg) + except: + isfile = False + if isfile: + c.setopt(c.HTTPPOST, [("file", (c.FORM_FILE, encode_cfg))]) + else: + # use as binary data + c.setopt(c.HTTPPOST, [ + ('fileupload', ( + c.FORM_BUFFER, '{sprog}_v{sver}.dmp'.format(sprog=os.path.basename(sys.argv[0]), sver=VER), + c.FORM_BUFFERPTR, encode_cfg + )), + ]) + + responsecode = 200 + try: + c.perform() + responsecode = c.getinfo(c.RESPONSE_CODE) + except Exception, e: + return e[0], e[1] + c.close() + + if responsecode>=400: + return responsecode, header.response() + elif header.contenttype()!='text/html': + return ExitCode.UPLOAD_CONFIG_ERROR, "Device did not response properly, may be Tasmota webserver admin mode is disabled (WebServer 2)" + + return 0, 'OK' + + +def DecryptEncrypt(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) + """ + if isinstance(obj, bytearray): + obj = str(obj) + dobj = obj[0:2] + for i in range(2, len(obj)): + dobj += chr( (ord(obj[i]) ^ (CONFIG_FILE_XOR +i)) & 0xff ) + return dobj + + def GetSettingsCrc(dobj): """ Return binary config data calclulated crc @@ -899,121 +1395,173 @@ def GetSettingsCrc(dobj): @param dobj: decrypted binary config data - @return: 2 byte unsigned integer crc value + @return: + 2 byte unsigned integer crc value """ + if isinstance(dobj, bytearray): + dobj = str(dobj) crc = 0 for i in range(0, len(dobj)): if not i in [14,15]: # Skip crc - crc += ord(dobj[i]) * (i+1) + byte = ord(dobj[i]) + crc += byte * (i+1) + return crc & 0xffff -def GetFieldFormat(fielddef): +def GetFieldDef(fielddef): + """ - Return the format item of field definition + Get the field def items @param fielddef: field format - see "Settings dictionary" above - @return: from fielddef[0] - + @return: + , , , , , + undefined items can be None """ - return fielddef[0] + _format = baseaddr = datadef = convert = None + bits = bitshift = 0 + if len(fielddef)==3: + # def without convert tuple + _format, baseaddr, datadef = fielddef + elif len(fielddef)==4: + # def with convert tuple + _format, baseaddr, datadef, convert = fielddef + + if isinstance(baseaddr, (list,tuple)): + baseaddr, bits, bitshift = baseaddr + + if isinstance(datadef, int): + # convert single int into list with one item + datadef = [datadef] + return _format, baseaddr, bits, bitshift, datadef, convert -def GetFieldBaseAddr(fielddef): - """ - Return the format item of field definition - - @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): +def MakeFieldBaseAddr(baseaddr, bits, bitshift): """ Return a based on given arguments @param baseaddr: baseaddr from Settings definition - @param bitlen: - 0 or bitlen + @param bits: + 0 or bits @param bitshift: 0 or bitshift - @return: (,,) if bitlen != 0 - baseaddr if bitlen == 0 + @return: + (,,) if bits != 0 + baseaddr if bits == 0 """ - if bitlen!=0: - return (baseaddr, bitlen, bitshift) + if bits!=0: + return (baseaddr, bits, bitshift) return baseaddr -def ConvertFieldValue(value, fielddef, raw=False): +def ConvertFieldValue(value, fielddef, read=True, raw=False): """ Convert field value based on field desc @param value: - original value read from binary data + original value @param fielddef field definition - see "Settings dictionary" above + @param read + use read conversion if True, otherwise use write conversion @param raw return raw values (True) or converted values (False) - @return: (un)converted value + @return: + (un)converted value """ - if not raw and len(fielddef)>3: - 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) + _format, baseaddr, bits, bitshift, datadef, convert = GetFieldDef(fielddef) + + # call password functions even if raw value should be processed + if callable(convert) and (convert==passwordread or convert==passwordwrite): + raw = False + if isinstance(convert, (list,tuple)) and len(convert)>0 and (convert[0]==passwordread or convert[0]==passwordwrite): + raw = False + if isinstance(convert, (list,tuple)) and len(convert)>1 and (convert[1]==passwordread or convert[1]==passwordwrite): + raw = False + + if not raw and convert is not None: + if isinstance(convert, (list,tuple)): # extract read conversion if tuple is given + if read: + convert = convert[0] + else: + convert = convert[1] + try: + if isinstance(convert, str): # evaluate strings + return eval(convert.replace('$','value')) + elif callable(convert): # use as format function + return convert(value) + except: + pass + return value -def GetFieldLength(fielddef): +def GetFieldMinMax(fielddef): """ - Return length of a field in bytes based on field format definition + Get minimum, maximum of field based on field format definition @param fielddef: field format - see "Settings dictionary" above - @return: length of field in bytes + @return: + min, max + """ + minmax = {'c': (0, 1), + '?': (0, 1), + 'b': (~0x7f, 0x7f), + 'B': (0, 0xff), + 'h': (~0x7fff, 0x7fff), + 'H': (0, 0xffff), + 'i': (~0x7fffffff, 0x7fffffff), + 'I': (0, 0xffffffff), + 'l': (~0x7fffffff, 0x7fffffff), + 'L': (0, 0xffffffff), + 'q': (~0x7fffffffffffffff, 0x7fffffffffffffff), + 'Q': (0, 0x7fffffffffffffff), + 'f': (sys.float_info.min, sys.float_info.max), + 'd': (sys.float_info.min, sys.float_info.max), + } + _format, baseaddr, bits, bitshift, datadef, convert = GetFieldDef(fielddef) + _min = 0 + _max = 0 + + if _format[-1:] in minmax: + _min, _max = minmax[_format[-1:]] + elif _format[-1:] in ['s','p']: + # s and p may have a prefix as length + match = re.search("\s*(\d+)", _format) + if match: + _max=int(match.group(0)) + return _min,_max + +def GetFieldLength(fielddef): + """ + Get 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] + _format, baseaddr, bits, bitshift, datadef, convert = GetFieldDef(fielddef) if datadef is not None: - # fielddef[2] contains a array or int + # datadef contains a list # 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 ): + if isinstance(datadef, list): + for i in range(0, datadef[0]): # multidimensional array if isinstance(datadef, list) and len(datadef)>1: @@ -1024,35 +1572,62 @@ def GetFieldLength(fielddef): length += GetFieldLength( (fielddef[0], fielddef[1], None) ) else: - if isinstance(fielddef[0], dict): - # -> iterate through format_ - addr = -1 - setting = fielddef[0] + if isinstance(_format, dict): + # -> iterate through _format + addr = None + setting = _format for name in setting: - baseaddr, bitlen, bitshift = GetFieldBaseAddr(setting[name]) - len_ = GetFieldLength(setting[name]) + _dummy1, baseaddr, bits, bitshift, _dummy2, _dummy3 = GetFieldDef(setting[name]) + _len = GetFieldLength(setting[name]) if addr != baseaddr: addr = baseaddr - length += len_ + length += _len else: - if format_[-1:].lower() in ['b','c','?']: + if _format[-1:] in ['b','B','c','?']: length=1 - elif format_[-1:].lower() in ['h']: + elif _format[-1:] in ['h','H']: length=2 - elif format_[-1:].lower() in ['i','l','f']: + elif _format[-1:] in ['i','I','l','L','f']: length=4 - elif format_[-1:].lower() in ['q','d']: + elif _format[-1:] in ['q','Q','d']: length=8 - elif format_[-1:].lower() in ['s','p']: + elif _format[-1:] in ['s','p']: # s and p may have a prefix as length - match = re.search("\s*(\d+)", format_) + match = re.search("\s*(\d+)", _format) if match: length=int(match.group(0)) return length +def GetSubfieldDef(fielddef): + """ + Get subfield definition from a given field definition + + @param fielddef: + see Settings desc above + + @return: + subfield definition + """ + subfielddef = None + + _format, baseaddr, bits, bitshift, datadef, convert = GetFieldDef(fielddef) + if isinstance(datadef, list) and len(datadef)>1: + if len(fielddef)<4: + subfielddef = (_format, MakeFieldBaseAddr(baseaddr, bits, bitshift), datadef[1:]) + else: + subfielddef = (_format, MakeFieldBaseAddr(baseaddr, bits, bitshift), datadef[1:], convert) + # single array + else: + if len(fielddef)<4: + subfielddef = (_format, MakeFieldBaseAddr(baseaddr, bits, bitshift), None) + else: + subfielddef = (_format, MakeFieldBaseAddr(baseaddr, bits, bitshift), None, convert) + return subfielddef + + def GetField(dobj, fieldname, fielddef, raw=False, addroffset=0): """ Get field value from definition @@ -1068,206 +1643,530 @@ def GetField(dobj, fieldname, fielddef, raw=False, addroffset=0): @param addroffset use offset for baseaddr (used for recursive calls) - @return: read field value + @return: + read field value """ + if isinstance(dobj, bytearray): + dobj = str(dobj) + result = None - # get format from field definition - format_ = GetFieldFormat(fielddef) + # get field definition + _format, baseaddr, bits, bitshift, datadef, convert = GetFieldDef(fielddef) - # get baseaddr from field definition - baseaddr, bitlen, bitshift = GetFieldBaseAddr(fielddef) - - # get datadef from field definition - datadef = None - if fielddef is not None and len(fielddef)>2: - datadef = fielddef[2] - - if datadef is not None: + # contains a integer list + if isinstance(datadef, list): result = [] + offset = 0 + for i in range(0, datadef[0]): + subfielddef = GetSubfieldDef(fielddef) + length = GetFieldLength(subfielddef) + if length != 0 and (fieldname != 'raw' or args.jsonrawkeys): + result.append(GetField(dobj, fieldname, subfielddef, raw=raw, addroffset=addroffset+offset)) + offset += length - # 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): + # contains a dict + elif isinstance(_format, dict): + config = {} + for name in _format: # -> iterate through _format + if name != 'raw' or args.jsonrawkeys: + config[name] = GetField(dobj, name, _format[name], raw=raw, addroffset=addroffset) + result = config - offset = 0 - for i in range(0, datadef[0] if isinstance(datadef, list) else datadef): + # a simple value + elif isinstance(_format, (str, bool, int, float, long)): + if GetFieldLength(fielddef) != 0: + result = struct.unpack_from(_format, dobj, baseaddr+addroffset)[0] - # multidimensional array - 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 + if not _format[-1:].lower() in ['s','p']: + if bitshift>=0: + result >>= bitshift else: - if len(fielddef)<4: - subfielddef = (fielddef[0], MakeFieldBaseAddr(baseaddr, bitlen, bitshift), None) - else: - subfielddef = (fielddef[0], MakeFieldBaseAddr(baseaddr, bitlen, bitshift), None, fielddef[3]) + result <<= abs(bitshift) + if bits>0: + result &= (1< 127 + result = unicode(s, errors='ignore') + + result = ConvertFieldValue(result, fielddef, read=True, raw=raw) else: - # contains a dict - if isinstance(fielddef[0], dict): - # -> iterate through format_ - setting = fielddef[0] - config = {} - for name in setting: - if name != 'raw' or args.rawkeys: - config[name] = GetField(dobj, name, setting[name], raw=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) + exit(ExitCode.INTERNAL_ERROR, "Wrong mapping format definition: '{}'".format(_format), typ=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) return result -def DeEncrypt(obj): +def SetField(dobj, fieldname, fielddef, restore, raw=False, addroffset=0, filename=""): """ - Decrpt/Encrypt binary config data + Get field value from definition - @param obj: - binary config data - - @return: decrypted configuration (if obj contains encrypted data) - encrypted configuration (if obj contains decrypted data) + @param dobj: + decrypted binary config data + @param fieldname: + name of the field + @param fielddef: + see Settings desc above + @param raw + handle values as raw values (True) or converted (False) + @param addroffset + use offset for baseaddr (used for recursive calls) + @param restore + restore mapping with the new value(s) """ - dobj = obj[0:2] - for i in range(2, len(obj)): - dobj += chr( (ord(obj[i]) ^ (CONFIG_FILE_XOR +i)) & 0xff ) + _format, baseaddr, bits, bitshift, datadef, convert = GetFieldDef(fielddef) + fieldname = str(fieldname) + + # do not write readonly values + if isinstance(convert, (list,tuple)) and len(convert)>1 and convert[1]==None: + if args.debug: + print >> sys.stderr, "SetField(): Readonly '{}' using '{}'/{}{} @{} skipped".format(fieldname, _format, datadef, bits, hex(baseaddr+addroffset)) + return dobj + + # contains a list + if isinstance(datadef, list): + offset = 0 + if len(restore)>datadef[0]: + exit(ExitCode.RESTORE_DATA_ERROR, "file '{sfile}', array '{sname}[{selem}]' exceeds max number of elements [{smax}]".format(sfile=filename, sname=fieldname, selem=len(restore), smax=datadef[0]), typ=LogType.WARNING, doexit=not args.ignorewarning, line=inspect.getlineno(inspect.currentframe())) + for i in range(0, datadef[0]): + subfielddef = GetSubfieldDef(fielddef) + length = GetFieldLength(subfielddef) + if length != 0: + if i>=len(restore): # restore data list may be shorter than definition + break + try: + subrestore = restore[i] + dobj = SetField(dobj, fieldname, subfielddef, subrestore, raw=raw, addroffset=addroffset+offset, filename=filename) + except: + pass + offset += length + + # contains a dict + elif isinstance(_format, dict): + for name in _format: # -> iterate through _format + if name in restore: + dobj = SetField(dobj, name, _format[name], restore[name], raw=raw, addroffset=addroffset, filename=filename) + + # a simple value + elif isinstance(_format, (str, bool, int, float, long)): + valid = True + err = "outside range" + + _min, _max = GetFieldMinMax(fielddef) + value = _value = valid = None + # simple one value + if _format[-1:] in ['c']: + try: + value = ConvertFieldValue(restore.encode(STR_ENCODING)[0], fielddef, read=False, raw=raw) + except: + valid = False + # bool + elif _format[-1:] in ['?']: + try: + value = ConvertFieldValue(bool(restore), fielddef, read=False, raw=raw) + except: + valid = False + # integer + elif _format[-1:] in ['b','B','h','H','i','I','l','L','q','Q','P']: + try: + value = ConvertFieldValue(restore, fielddef, read=False, raw=raw) + if isinstance(value, (str, unicode)): + value = int(value, 0) + else: + value = int(value) + # bits + if bits!=0: + value = struct.unpack_from(_format, dobj, baseaddr+addroffset)[0] + bitvalue = int(restore) + mask = (1<mask: + _min = 0 + _max = mask + _value = bitvalue + valid = False + else: + if bitshift>=0: + bitvalue <<= bitshift + mask <<= bitshift + else: + bitvalue >>= abs(bitshift) + mask >>= abs(bitshift) + value &= (0xffffffff ^ mask) + value |= bitvalue + else: + _value = value + except: + valid = False + # float + elif _format[-1:] in ['f','d']: + try: + value = ConvertFieldValue(float(restore), fielddef, read=False, raw=raw) + except: + valid = False + # string + elif _format[-1:] in ['s','p']: + try: + value = ConvertFieldValue(restore.encode(STR_ENCODING), fielddef, read=False, raw=raw) + # be aware 0 byte at end of string (str must be < max, not <= max) + _max -= 1 + valid = (len(value)>=_min) and (len(value)<=_max) + err = "string exceeds max length" + except: + valid = False + + if value is None: + valid = False + if valid is None: + valid = (value>=_min) and (value<=_max) + if _value is None: + _value = value + if isinstance(value, (str, unicode)): + _value = "'{}'".format(_value) + + if valid: + if args.debug: + if bits: + sbits=" {} bits shift {}".format(bits, bitshift) + else: + sbits = "" + print >> sys.stderr, "SetField(): Set '{}' using '{}'/{}{} @{} to {}".format(fieldname, _format, datadef, sbits, hex(baseaddr+addroffset), _value) + struct.pack_into(_format, dobj, baseaddr+addroffset, value) + else: + exit(ExitCode.RESTORE_DATA_ERROR, "file '{sfile}', value for name '{sname}': {svalue} {serror} [{smin},{smax}]".format(sfile=filename, sname=fieldname, serror=err, svalue=_value, smin=_min, smax=_max), typ=LogType.WARNING, doexit=not args.ignorewarning) + return dobj -def GetTemplateSetting(version): +def Bin2Mapping(decode_cfg, raw=True): """ - Search for template, settings and size to be used depending on given version number + Decodes binary data stream into pyhton mappings dict - @param version: - version number from read binary data to search for - - @return: template, settings to use, None if version is invalid - """ - # search setting definition - template = None - setting = None - size = None - for cfg in Settings: - if version >= cfg[0]: - template = cfg - size = template[1] - setting = template[2] - break - - return template, size, setting - - -def Decode(obj, raw=True): - """ - Decodes binary data stream - - @param obj: + @param decode_cfg: binary config data (decrypted) - @param raw + @param raw: decode raw values (True) or converted values (False) - @return: configuration dictionary + @return: + config data as mapping dictionary """ - # get header data - version = GetField(obj, 'version', Setting_6_2_1['version'], raw=True) + if isinstance(decode_cfg, bytearray): + decode_cfg = str(decode_cfg) + + # get binary header and template to use + version, template, size, setting = GetTemplateSetting(decode_cfg) - template, size, setting = GetTemplateSetting(version) # if we did not found a mathching setting if template is None: - exit(2, "Tasmota configuration version 0x{:x} not supported".format(version) ) + exit(ExitCode.UNSUPPORTED_VERSION, "Tasmota configuration version 0x{:x} not supported".format(version),line=inspect.getlineno(inspect.currentframe())) # check size if exists if 'cfg_size' in setting: - cfg_size = GetField(obj, 'cfg_size', setting['cfg_size'], raw=True) + cfg_size = GetField(decode_cfg, 'cfg_size', setting['cfg_size'], raw=True) # read size should be same as definied in template if cfg_size > size: # 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) + exit(ExitCode.DATA_SIZE_MISMATCH, "Number of bytes read does ot match - read {}, expected {} byte".format(cfg_size, template[1]), typ=LogType.ERROR,line=inspect.getlineno(inspect.currentframe())) elif cfg_size < size: # 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') + exit(ExitCode.DATA_SIZE_MISMATCH, "Number of bytes read to small to process - read {}, expected {} byte".format(cfg_size, template[1]), typ=LogType.ERROR,line=inspect.getlineno(inspect.currentframe())) # check crc if exists if 'cfg_crc' in setting: - cfg_crc = GetField(obj, 'cfg_crc', setting['cfg_crc'], raw=True) + cfg_crc = GetField(decode_cfg, 'cfg_crc', setting['cfg_crc'], raw=True) else: - cfg_crc = GetSettingsCrc(obj) - if cfg_crc != GetSettingsCrc(obj): - exit(4, 'Data CRC error, read 0x{:x} should be 0x{:x}'.format(cfg_crc, GetSettingsCrc(obj)), typ='WARNING', doexit=args.exitonwarning) + cfg_crc = GetSettingsCrc(decode_cfg) + if cfg_crc != GetSettingsCrc(decode_cfg): + exit(ExitCode.DATA_CRC_ERROR, 'Data CRC error, read 0x{:x} should be 0x{:x}'.format(cfg_crc, GetSettingsCrc(decode_cfg)), typ=LogType.WARNING, doexit=not args.ignorewarning,line=inspect.getlineno(inspect.currentframe())) # get config - config = GetField(obj, None, (setting,None,None), raw=raw) + config = GetField(decode_cfg, None, (setting,None,None), raw=raw) # 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, + config['header'] = {'timestamp':timestamp.strftime("%Y-%m-%d %H:%M:%S"), + 'format': { + 'jsonindent': args.jsonindent, + 'jsoncompact': args.jsoncompact, + 'jsonsort': args.jsonsort, + 'jsonrawvalues':args.jsonrawvalues, + 'jsonrawkeys': args.jsonrawkeys, + 'jsonhidepw': args.jsonhidepw, + }, + 'src': { + 'crc': hex(cfg_crc), + 'size': cfg_size, + 'version': hex(version), + }, + 'data': { + 'crc': hex(GetSettingsCrc(decode_cfg)), + 'size': len(decode_cfg), + 'version': hex(template[0]), + }, + 'script': { + 'name': os.path.basename(__file__), + 'version': VER, + }, + 'os': (platform.machine(), platform.system(), platform.release(), platform.version(), platform.platform()), + 'python': platform.python_version(), } 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.') +def Mapping2Bin(decode_cfg, jsonconfig, filename=""): + """ + Encodes into binary data stream - source = parser.add_argument_group('source') - source.add_argument('-f', '--file', + @param decode_cfg: + binary config data (decrypted) + @param jsonconfig: + restore data mapping + @param filename: + name of the restore file (for error output only) + + @return: + changed binary config data (decrypted) + """ + if isinstance(decode_cfg, str): + decode_cfg = bytearray(decode_cfg) + + + # get binary header data to use the correct version template from device + version, template, size, setting = GetTemplateSetting(decode_cfg) + + _buffer = bytearray() + _buffer.extend(decode_cfg) + + if template is not None: + try: + raw = jsonconfig['header']['format']['jsonrawvalues'] + except: + if 'header' not in jsonconfig: + errkey = 'header' + elif 'format' not in jsonconfig['header']: + errkey = 'header.format' + elif 'jsonrawvalues' not in jsonconfig['header']['format']: + errkey = 'header.format.jsonrawvalues' + exit(ExitCode.RESTORE_DATA_ERROR, "Restore file '{sfile}' name '{skey}' missing, don't know how to evaluate restore data!".format(sfile=filename, skey=errkey), typ=LogType.ERROR, doexit=not args.ignorewarning) + + # iterate through restore data mapping + for name in jsonconfig: + # key must exist in both dict + if name in setting: + SetField(_buffer, name, setting[name], jsonconfig[name], raw=raw, addroffset=0, filename=filename) + else: + if name != 'header': + exit(ExitCode.RESTORE_DATA_ERROR, "Restore file '{}' contains obsolete name '{}', skipped".format(filename, name), typ=LogType.WARNING, doexit=not args.ignorewarning) + + crc = GetSettingsCrc(_buffer) + struct.pack_into(setting['cfg_crc'][0], _buffer, setting['cfg_crc'][1], crc) + return _buffer + + else: + exit(ExitCode.UNSUPPORTED_VERSION,"File '{}', Tasmota configuration version 0x{:x} not supported".format(filename, version), typ=LogType.WARNING, doexit=not args.ignorewarning) + + return decode_cfg + + +def Backup(backupfile, backupfileformat, encode_cfg, decode_cfg, configuration): + """ + Create backup file + + @param backupfile: + Raw backup filename from program args + @param backupfileformat: + Backup file format + @param encode_cfg: + binary config data (encrypted) + @param decode_cfg: + binary config data (decrypted) + @param configuration: + config data mapppings + """ + + backupfileformat = args.backupfileformat + try: + name, ext = os.path.splitext(backupfile) + if ext.lower() == '.'+FileType.BIN.lower(): + backupfileformat = FileType.BIN + elif ext.lower() == '.'+FileType.DMP.lower(): + backupfileformat = FileType.DMP + elif ext.lower() == '.'+FileType.JSON.lower(): + backupfileformat = FileType.JSON + except: + pass + + fileformat = "" + # binary format + if backupfileformat.lower() == FileType.BIN.lower(): + fileformat = "binary" + backup_filename = MakeFilename(backupfile, FileType.BIN, configuration) + try: + backupfp = open(backup_filename, "wb") + magic = BINARYFILE_MAGIC + backupfp.write(struct.pack('> sys.stderr, parser.format_values() + print >> sys.stderr, "Settings:" + for k in args.__dict__: + print >> sys.stderr, " "+str(k), "= ",eval('args.{}'.format(k)) + return args + + +if __name__ == "__main__": + args = ParseArgs() + if args.shorthelp: + ShortHelp() + # default no configuration available - configobj = None + encode_cfg = 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") + exit(ExitCode.ARGUMENT_ERROR, "Unable to select source, do not use -d and -f together",line=inspect.getlineno(inspect.currentframe())) - # read config direct from device via http - if args.device is not None: + # pull config from Tasmota device/file + encode_cfg = PullTasmotaConfig() + if encode_cfg is None: + # no config source given + ShortHelp(False) + print + print parser.epilog + sys.exit(ExitCode.OK) - 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) ) + if len(encode_cfg) == 0: + exit(ExitCode.FILE_READ_ERROR, "Unable to read configuration data from {} '{}'".format('device' if args.device is not None else 'file', \ + args.device if args.device is not None else args.tasmotafile) \ + ,line=inspect.getlineno(inspect.currentframe()) ) + # decrypt Tasmota config + decode_cfg = DecryptEncrypt(encode_cfg) - configobj = buffer.getvalue() + # decode into mappings dictionary + configuration = Bin2Mapping(decode_cfg, args.jsonrawvalues) - # read config from a file - elif args.tasmotafile is not None: + # backup to file + if args.backupfile is not None: + Backup(args.backupfile, args.backupfileformat, encode_cfg, decode_cfg, configuration) - if not os.path.isfile(args.tasmotafile): # check file exists - exit(1, "File '{}' not found".format(args.tasmotafile)) - try: - tasmotafile = open(args.tasmotafile, "rb") - configobj = tasmotafile.read() - tasmotafile.close() - except Exception, e: - exit(e[0], e[1]) + # restore from file + if args.restorefile is not None: + Restore(args.restorefile, encode_cfg, decode_cfg, configuration) - # no config source given - else: - parser.print_help() - sys.exit(0) - - if configobj is not None and len(configobj)>0: - cfg = DeEncrypt(configobj) - - configuration = Decode(cfg, args.rawvalues) - - # output to file - if args.outputfile is not None: - outputfilename = GetFilenameReplaced(args.outputfile, configuration) - if args.outputfileformat == 'binary': - outputfile = open(outputfilename, "wb") - outputfile.write(struct.pack(' Date: Sat, 27 Oct 2018 11:37:42 +0200 Subject: [PATCH 10/16] Change OTA Url * Change default OTA Url to http://thehackbox.org/tasmota/release/sonoff.bin (#4170) * Add Tuya Software Serial to support additional Tuya configurations (#4178) --- README.md | 23 +++-- RELEASENOTES.md | 162 ++++++++++++++++++---------------- sonoff/_changelog.ino | 2 + sonoff/my_user_config.h | 4 +- sonoff/xdrv_16_tuyadimmer.ino | 88 +++++++++--------- 5 files changed, 152 insertions(+), 127 deletions(-) diff --git a/README.md b/README.md index 524ad49ab..cf9d79d5a 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ If you like **Sonoff-Tasmota**, give it a star, or fork it and contribute! [![GitHub forks](https://img.shields.io/github/forks/arendst/Sonoff-Tasmota.svg?style=social&label=Fork)](https://github.com/arendst/Sonoff-Tasmota/network) [![donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://paypal.me/tasmota) +See [RELEASENOTES.md](https://github.com/arendst/Sonoff-Tasmota/blob/development/RELEASENOTES.md) for release information. + +In addition to the [release webpage](https://github.com/arendst/Sonoff-Tasmota/releases/latest) the binaries can also be OTA downloaded from http://thehackbox.org/tasmota/release/ + ### Development [![Dev Version](https://img.shields.io/badge/development%20version-6.2.1.x-blue.svg)](https://github.com/arendst/Sonoff-Tasmota) [![Download Dev](https://img.shields.io/badge/download-development-yellow.svg)](http://thehackbox.org/tasmota/) @@ -114,21 +118,25 @@ You can contribute to Sonoff-Tasmota by Libraries used with Sonoff-Tasmota are: - [ESP8266 core for Arduino](https://github.com/esp8266/Arduino) - [Adafruit CCS811](https://github.com/adafruit/Adafruit_CCS811) +- [Adafruit ILI9341](https://github.com/adafruit/Adafruit_ILI9341) +- [Adafruit LED Backpack](https://github.com/adafruit/Adafruit-LED-Backpack-Library) - [Adafruit SGP30](https://github.com/adafruit/Adafruit_SGP30) +- [Adafruit SSD1306](https://github.com/adafruit/Adafruit_SSD1306) +- [Adafruit GFX](https://github.com/adafruit/Adafruit-GFX-Library) - [ArduinoJson](https://arduinojson.org/) +- [arduino mqtt](https://github.com/256dpi/arduino-mqtt) - [Bosch BME680](https://github.com/BoschSensortec/BME680_driver) - [C2 Programmer](http://app.cear.ufpb.br/~lucas.hartmann/tag/efm8bb1/) -- [Esp8266MqttClient](https://github.com/tuanpmt/ESP8266MQTTClient) - [esp-knx-ip](https://github.com/envy/esp-knx-ip) -- [esp-mqtt-arduino](https://github.com/i-n-g-o/esp-mqtt-arduino) -- [ESPAsyncUDP](https://github.com/me-no-dev/ESPAsyncUDP) - [I2Cdevlib](https://github.com/jrowberg/i2cdevlib) - [IRremoteEsp8266](https://github.com/markszabo/IRremoteESP8266) - [JobaTsl2561](https://github.com/joba-1/Joba_Tsl2561) +- [Liquid Cristal](https://github.com/marcoschwartz/LiquidCrystal_I2C) - [MultiChannelGasSensor](http://wiki.seeedstudio.com/Grove-Multichannel_Gas_Sensor/) - [NeoPixelBus](https://github.com/Makuna/NeoPixelBus) - [OneWire](https://github.com/PaulStoffregen/OneWire) - [PubSubClient](https://github.com/knolleary/pubsubclient) +- [rc-switch](https://github.com/sui77/rc-switch) #### People inspiring me People helping to keep the show on the road: @@ -142,16 +150,19 @@ People helping to keep the show on the road: - Flexiti for his initial timer implementation - reloxx13 for his [TasmoAdmin](https://github.com/reloxx13/TasmoAdmin) management tool - Joachim Banzhaf for his TSL2561 library and driver -- Gijs Noorlander for his MHZ19 and SenseAir drivers +- Gijs Noorlander for his MHZ19, SenseAir and updated PubSubClient drivers - Emontnemery for his HomeAssistant Discovery concept and many code tuning tips - Aidan Mountford for his HSB support - Daniel Ztolnai for his Serial Bridge implementation -- Gerhard Mutz for his SGP30 and Sunrise/Sunset driver +- Gerhard Mutz for his SGP30, Sunrise/Sunset and display support drivers - Nuno Ferreira for his HC-SR04 driver - Adrian Scillato for his (security)fixes and implementing and maintaining KNX - Gennaro Tortone for implementing and maintaining Eastron drivers - Raymond Mouthaan for managing Wemos Wiki information -- Norbert Richter, Frogmore42 and Jason2866 for providing many issue answers +- Norbert Richter for his decode-config.py tool +- Andre Thomas for providing [thehackbox](http://thehackbox.org/tasmota/) OTA support and daily development builds +- Joel Stein and digiblur for their Tuya research and driver +- Frogmore42 and Jason2866 for providing many issue answers - Many more providing Tips, Pocs or PRs ### License diff --git a/RELEASENOTES.md b/RELEASENOTES.md index a98f96b33..b7a1537bb 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -15,7 +15,7 @@ To save memory space all other binaries support **WifiManager only**. See _changelog.ino how to enable them again. - Define WIFI_CONFIG_TOOL now contains the default behaviour once a SSID has been configured. - If no SSID is configured making a wifi connection impossible the new define WIFI_CONFIG_NO_SSID will be used. -- While define WIFI_CONFIG_NO_SSID is set to WIFI_WPSCONFIG in user_config.h the compiler will check for define USE_WPS and if not enabled WIFI_CONFIG_NO_SSID will default to WIFI_MANAGER using the webserver. If define USE_WEBSERVER is also not enabled WIFI_CONFIG_NO_SSID will default to WIFI_SMARTCONFIG. If define USE_SMARTCONFIG is also not enabled WIFI_CONFIG_NO_SSID will default to a new option WIFI_SERIAL allowing to enter wifi parameters to serial which is always possible. +- While define WIFI_CONFIG_NO_SSID is set to WIFI_WPSCONFIG in my_user_config.h the compiler will check for define USE_WPS and if not enabled WIFI_CONFIG_NO_SSID will default to WIFI_MANAGER using the webserver. If define USE_WEBSERVER is also not enabled WIFI_CONFIG_NO_SSID will default to WIFI_SMARTCONFIG. If define USE_SMARTCONFIG is also not enabled WIFI_CONFIG_NO_SSID will default to a new option WIFI_SERIAL allowing to enter wifi parameters to serial which is always possible. ## Provided Binary Downloads The following binary downloads have been compiled with ESP8266/Arduino library version **2.3.0** @@ -25,6 +25,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library v - **sonoff.bin** = The Sonoff version without Wps and SmartConfig configuration but adds more sensors. - **sonoff-BG.bin** to **sonoff-TW.bin** = The Sonoff version without Wps and SmartConfig configuration in different languages. - **sonoff-sensors.bin** = The Sensors version without Wps and SmartConfig configuration but adds even more useful sensors. +- **sonoff-display.bin** = The Display version without Wps and SmartConfig configuration but adds display support. - **sonoff-knx.bin** = The Knx version without Wps and SmartConfig configuration and some other features but adds KNX support. See [Tasmota ESP/Arduino library version related issues](https://github.com/arendst/Sonoff-Tasmota/wiki/Theo's-Tasmota-Tips#20180523---relation-tasmota-and-esp8266arduino-core-version) why these files are still released using ESP/Arduino library version v2.3.0. @@ -33,8 +34,8 @@ See [Tasmota ESP/Arduino library version related issues](https://github.com/aren | Feature or Sensor | minimal | classic | sonoff | knx | sensors | Remarks |--------------------------------|---------|---------|--------|------|---------|-------- -| ESP/Arduino lib v2.3.0 | 340k | 477k | 473k | 492k | 497k | -| ESP/Arduino lib v2.4.2 | 360k | 491k | 491k | 509k | 513k | No sleep +| ESP/Arduino lib v2.3.0 | 344k | 485k | 491k | 510k | 516k | +| ESP/Arduino lib v2.4.2 | 363k | 499k | 509k | 526k | 532k | No sleep | | | | | | | | MY_LANGUAGE en-GB | x | x | x | x | x | | USE_WPS | - | x | - | - | - | WPS @@ -55,7 +56,7 @@ See [Tasmota ESP/Arduino library version related issues](https://github.com/aren | USE_SUNRISE | - | - | x | x | x | | USE_RULES | - | - | x | x | x | | | | | | | | -| USE_ADC_VCC | x | x | x | x | | +| USE_ADC_VCC | x | x | x | x | - | | USE_DS18B20 | - | - | - | - | - | Single sensor | USE_DS18x20 | - | x | x | x | x | Multiple sensors | USE_DS18x20_LEGACY | - | - | - | - | - | Multiple sensors @@ -111,76 +112,89 @@ See [Tasmota ESP/Arduino library version related issues](https://github.com/aren | USE_RF_FLASH | - | - | x | x | x | | USE_TUYA_DIMMER | - | - | x | x | x | | USE_TX20_WIND_SENSOR | - | - | x | x | x | +| USE_RC_SWITCH | - | - | x | x | x | | USE_DISPLAY | - | - | - | - | - | ## Changelog -Version 6.2.1 20180905 - * Fix possible ambiguity on command parameters if StateText contains numbers only (#3656) - * Fix Wemo emulation to select the first relay when more than one relay is present (#3657) - * Fix possible exception due to buffer overflow (#3659) - * Fix lost energy today and total energy value after power cycle (#3689) - -Version 6.2.0 20180901 - * Allow user override of define MAX_RULE_VARS and MAX_RULE_TIMERS (#3561) - * Disable wifi sleep for both Esp8266/Arduino core 2.4.1 and 2.4.2 to solve device freeze caused by Espressif SDK bug (#3554) - * Change DS18B20 driver to provide better instant results - * Change some sensor drivers to provide instant results - * Change define USE_ALL_SENSORS to USE_SENSORS as it doesn't contain all sensors due to duplicate I2C addresses - * Change some sensor update timings: AdcEvery 200 -> 250, Senseair 300 -> 250, SDM120 300 -> 250, SDM630 300 -> 250 - * Change default Wifi config option from WPS to Wifi Manager if WPS is disabled or Wifi Smartconfig if webserver is disabled or Wifi Serial input if Smartconfig is disabled - * Change SHT1x driver to provide better instant results and fix I2C interference - * Change DHT driver to provide better instant results and add decimals to DHT11 (#3164) - * Change DS18x20 driver to provide better instant results (#3169) - * Change CounterType 1 from milliseconds to microseconds (#3437) - * Change scheduler for better sleep support using Uptime, Delay, PulseTime and TelePeriod, Blinktime (#3581) - * Remove unused functionality from Sonoff-minimal to save space - * Remove WPS and SmartConfig from sonoff-minimal saving 56k code space - * Remove TSL2561 debug message and update library (#2415) - * Remove forced restart when sleep command is executed (#3554) - * Fix invalid response using more than 4 switches and domoticz - * Fix sonoff-minimal not using default settings - * Fix unsecure main webpage update - * Fix DHT driver mixing values for different sensors (#1797) - * Fix EnergyReset3 regression not clearing total energy (#2723) - * Fix rules once regression from v6.1.0 (#3198, #3226) - * Fix command Scale buffer overflow (#3236) - * Fix possible WDT due to long MQTT publish handling (#3313) - * Fix command TimeDst/TimeStd invalid JSON (#3322) - * Fix handling of default names when using names starting with shortcut character ",0,1 or 2 (#3392, #3600, #3618) - * Fix LM75AD I2C sensor detection (#3408) - * Fix iFan02 power on state (#3412, #3530) - * Fix some Pow R2 and S31 checksum errors using optimized re-sync (#3425) - * Fix SDM120 reporting wrong negative values to Domoticz (#3521) - * Fix MQTT reconnection detection when using TasmotaMqtt library (#3558) - * Fix OtaMagic when file path contains a dash (-) (#3563) - * Fix Sonoff Bridge data reception when using Portisch EFM8 firmware using in data buffer length (#3605) - * Add read sensor retry to DS18B20, DS18x20, DHT, SHT1X and HTU21 - * Add user selection of Wifi Smartconfig as define USE_SMARTCONFIG in user_config.h - * Add boot loop detection and perform some solutions - * Add wifi and mqtt status led blinkyblinky to be disabled by SetOption31 1. Does not work when LedPower is On (deliberate) (#871, #2230, #3114, #3155) - * Add support for TM1638 switch (#2226) - * Add GPIO options ButtonXn, SwitchXn and CounterXn to select INPUT mode instead of INPUT_PULLUP (#2525) - * Add support for APDS9960 proximity sensor (#3051) - * Add support for MPR121 controller in input mode for touch buttons (#3142) - * Add support for MCP230xx for general purpose input expansion and command Sensor29 (#3188) - * Add default Wifi Configuration tool as define WIFI_CONFIG_NO_SSID in user_config.h if no SSID is configured (#3224) - * Add command Timers 0/1 to globally disable or enable armed timers (#3270) - * Add support for CCS811 sensor (#3309) - * Add Turkish language file (#3332) - * Add command SerialSend4 to send binary serial data (#3345) - * Add initial support for sensor MPU6050 (#3352) - * Add rule triggers Wifi#Connected and Wifi#Disconnected (#3359) - * Add option + to command Rule to concatenate new rule with existing rules (#3365) - * Add message when JavaScript is not enabled in webbrowser (#3388) - * Add build time setting of ButtonTopic and SwitchTopic (#3414) - * Add iFan02 Fanspeed + and Fanspeed - command options (#3415) - * Add Individual HSBColorX commands (#3430, #3615) - * Add output support on MCP23008/MCP23017 (#3436) - * Add modulo option to rules like rule1 on Time#Minute|5 do backlog power on;delay 200;power off endon (#3466) - * Add RGB support for Domoticz (#3547) - * Add all ruletimer values to command RuleTimer result message (#3571) - * Add command Publish2 for publishing retained MQTT messages (#3593) - * Add commands ButtonDebounce 40..1000 and SwitchDebounce 40..1000 to have user control over debounce timing. Default is 50mS (#3594) - * Add RuleX debug options 8,9,10 (StopOnError) to control RuleX execution status after an exception restart (#3607) - * Add rule variables %sunrise%, %sunset%, %uptime% and %time% (#3608) - * Add optional MQTT_TELE_RETAIN to Energy Margins message (#3612, 3614) +Version 6.3.0 soon + * Change web Configure Module GPIO drop down list order for better readability + * Change status JSON message providing more switch and retain information + * Change xsns_17_senseair.ino to use TasmotaModbus library + * Change MCP230xx driver + * Change PubSubClient Mqtt library to non-blocking EspEasy version + * Change energy monitoring using energy sensor driver modules + * Change Webserver page handler for easier extension (thx to Adrian Scillato) + * Change pinmode for no-pullup defined switches to pullup when configured as switchmode PUSHBUTTON (=3 and up) (#3896) + * Change default OTA Url to http://thehackbox.org/tasmota/release/sonoff.bin (#4170) + * Remove support for MQTT Client esp-mqtt-arduino by #define MQTT_LIBRARY_TYPE MQTT_ESPMQTTARDUINO + * Remove commands PowerCal, VoltageCal and CurrentCal as more functionality is provided by commands PowerSet, VoltageSet and CurrentSet + * Remove restart after ntpserver change and force NTP re-sync (#3890) + * Fix showing Period Power in energy threshold messages + * Fix header file execution order by renaming user_config.h to my_user_config.h + * Fix some TSL2561 driver issues (#3681) + * Fix KNX PA exception. Regression from 6.2.1 buffer overflow caused by subStr() (#3700, #3710) + * Fix setting and getting color temperature for Philips Hue emulation (#3733) + * Fix ButtonRetain to not use default topic for clearing retain messages (#3737) + * Fix syslog when emulation is selected (#2109, #3784) + * Fix rule trigger POWER1#STATE execution after restart and SetOption0 is 0 (#3856) + * Fix Home Assistant forced light discovery (#3908) + * Fix invalid configuration restores and decode_config.py crc error when savedata = 0 (#3918) + * Fix timer offset -00:00 causing 12:00 hour offset (#3923) + * Fix I2CScan invalid JSON error message (#3925) + * Fix exception when wrong Domoticz JSON message is received (#3963) + * Fix Sonoff Bridge RfRaw receive (#4080, #4085) + * Fix possible wifi connection error (#4044, #4083) + * Fix invalid JSON floating point result from nan (Not a Number) and inf (Infinity) into null (#4147) + * Fix rule mqtt#connected trigger when mqtt is disabled (#4149) + * Add support for LCD, Matrix, TFT and Oled displays + * Add support for Neo Coolcam Wifi Smart Power Plug + * Add support for Michael Haustein ESP Switch + * Add support for MQTT Client based on lwmqtt to be selected by #define MQTT_LIBRARY_TYPE MQTT_ARDUINOMQTT + * Add support for Neo Coolcam Wifi Smart Power Plug + * Add support for Michael Haustein ESP Switch + * Add support for MQTT Client based on lwmqtt to be selected by #define MQTT_LIBRARY_TYPE MQTT_ARDUINOMQTT + * Add support for DS3231 Real Time Clock + * Add support for HX711 Load Cell with optional web GUI scale interface to demonstrate easy GUI plug-in + * Add support for serial 8N2 communication to TasmotaModbus and TasmotaSerial libraries + * Add support for RF transceiving using library RcSwitch (#2702) + * Add support for Shelly 1 and Shelly 2 (#2789) + * Add support for La Crosse TX20 Anemometer (#2654, #3146) + * Add support for MP3 player using DFRobot RB-DFR-562 (#3723) + * Add Support for Xiaomi-Philips Bulbs (#3787) + * Add support for PCA9685 12bit 16pin hardware PWM driver (#3866) + * Add support for EXS Relay V5.0 (#3810) + * Add support for OBI Power Socket (#1988, #3944) + * Add support for Teckin Power Socket with Energy Monitoring (#3950) + * Add support for Pzem-003/017 DC Energy monitoring module (#3694) + * Add support for Pzem-014/016 AC Energy monitoring module (#3694) + * Add support for CSL Aplic WDP 303075 Power Socket with Energy Monitoring (#3991, #3996) + * Add support for Tuya Dimmer (#469, #4075) + * Add command Display to show all settings at once + * Add command SerialSend5 to send raw serial data like "A5074100545293" + * Add command WebRefresh 1000..10000 to control web page refresh in milliseconds. Default is 2345 + * Add command WeightRes 0..3 to control display of decimals for kilogram + * Add command SetOption52 to control display of optional time offset from UTC in JSON messages (#3629, #3711) + * Add command RGBWWTable to support color calibration (#3933) + * Add command Reset 4 (reset to defaults but keep wifi params) and Reset 5 (as reset 4 and also erase flash) (#4061) + * Add authentication to HTTP web pages + * Add decimals as input to commands PowerSet, VoltageSet and CurrentSet + * Add tools/decode-config.py by Norbert Richter to decode configuration data. See file for information + * Add define USE_DISPLAYS for selecting image sonoff-display + * Add auto reload of main web page to some web restarts + * Add TasmotaModbus library as very basic modbus wrapper for TasmotaSerial + * Add more API callbacks and document API.md + * Add Wifi channel number to state message (#3664) + * Add user configurable GPIO02 and GPIO03 on H801 devices (#3692) + * Add network information to display start screen (#3704) + * Add toggle function RGBW lights (#3695, #3697) + * Add sleep to Nova Fitness SDS01X sensor (#2841, #3724, #3749) + * Add Analog input AD0 enabled to sonoff-sensors.bin (#3756, #3757) + * Add userid/password option to decode-status.py (#3796) + * Add power value below 5W to Sonoff Pow R2 and S31 (#3745) + * Add force_update to Home Assistant discovery (#3873) + * Add delay after restart before processing rule sensor data (#3811) + * Add rule triggers SWITCH1#BOOT and POWER1#BOOT (#3904, #3910) + * 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) + * Add Hebrew language file (#3960) + * Add whitespace removal from RfRaw and SerialSend5 (#4020) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 563e555d1..c043fbe1f 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -3,6 +3,8 @@ * Fix invalid JSON floating point result from nan (Not a Number) and inf (Infinity) into null (#4147) * Fix rule mqtt#connected trigger when mqtt is disabled (#4149) * Initial release of RF transceiving using library RcSwitch (#2702) + * Change default OTA Url to http://thehackbox.org/tasmota/release/sonoff.bin (#4170) + * Add Tuya Software Serial to support additional Tuya configurations (#4178) * * 6.2.1.18 20181019 * Add more API callbacks and document API.md diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index e970bcdf5..a086c6d62 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -79,7 +79,7 @@ #define WEB_LOG_LEVEL LOG_LEVEL_INFO // [WebLog] (LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE) // -- Ota ----------------------------------------- -#define OTA_URL "http://sonoff.maddox.co.uk/tasmota/sonoff.bin" // [OtaUrl] +#define OTA_URL "http://thehackbox.org/tasmota/release/sonoff.bin" // [OtaUrl] // -- MQTT ---------------------------------------- #define MQTT_USE 1 // [SetOption3] Select default MQTT use (0 = Off, 1 = On) @@ -384,7 +384,7 @@ #define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+2k code) -#define USE_RC_SWITCH // Add support for RF transceiver using library RcSwitch (+2k7 code) +#define USE_RC_SWITCH // Add support for RF transceiver using library RcSwitch (+2k7 code, 460 iram) /*********************************************************************************************\ * Debug features are only supported in development branch diff --git a/sonoff/xdrv_16_tuyadimmer.ino b/sonoff/xdrv_16_tuyadimmer.ino index 5b5a64062..eb700c322 100644 --- a/sonoff/xdrv_16_tuyadimmer.ino +++ b/sonoff/xdrv_16_tuyadimmer.ino @@ -20,8 +20,9 @@ #ifdef USE_TUYA_DIMMER #ifndef TUYA_DIMMER_ID -#define TUYA_DIMMER_ID 3 +#define TUYA_DIMMER_ID 3 #endif +#define TUYA_BUFFER_SIZE 256 #include @@ -33,6 +34,9 @@ uint8_t tuya_cmd_status = 0; // Current status of serial-read uint8_t tuya_cmd_checksum = 0; // Checksum of tuya command uint8_t tuya_data_len = 0; // Data lenght of command +char tuya_buffer[TUYA_BUFFER_SIZE]; // Serial receive buffer +int tuya_byte_counter = 0; // Index in serial receive buffer + boolean TuyaSetPower() { boolean status = false; @@ -103,27 +107,27 @@ void TuyaPacketProcess() { char scmnd[20]; - snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Packet Size=%d"), serial_in_byte_counter); + snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Packet Size=%d"), tuya_byte_counter); AddLog(LOG_LEVEL_DEBUG); - if (serial_in_byte_counter == 7 && serial_in_buffer[3] == 14 ) { // heartbeat packet + if (tuya_byte_counter == 7 && tuya_buffer[3] == 14 ) { // heartbeat packet AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: Heartbeat")); } - else if (serial_in_byte_counter == 12 && serial_in_buffer[3] == 7 && serial_in_buffer[5] == 5) { // on/off packet + else if (tuya_byte_counter == 12 && tuya_buffer[3] == 7 && tuya_buffer[5] == 5) { // on/off packet - snprintf_P(log_data, sizeof(log_data),PSTR("TYA: Rcvd - %s State"),serial_in_buffer[10]?"On":"Off"); + snprintf_P(log_data, sizeof(log_data),PSTR("TYA: Rcvd - %s State"),tuya_buffer[10]?"On":"Off"); AddLog(LOG_LEVEL_DEBUG); - if((power || Settings.light_dimmer > 0) && (power != serial_in_buffer[10])) { - ExecuteCommandPower(1, serial_in_buffer[10], SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction + if((power || Settings.light_dimmer > 0) && (power != tuya_buffer[10])) { + ExecuteCommandPower(1, tuya_buffer[10], SRC_SWITCH); // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction } } - else if (serial_in_byte_counter == 15 && serial_in_buffer[3] == 7 && serial_in_buffer[5] == 8) { // dim packet + else if (tuya_byte_counter == 15 && tuya_buffer[3] == 7 && tuya_buffer[5] == 8) { // dim packet - snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Rcvd Dim State=%d"), serial_in_buffer[13]); + snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Rcvd Dim State=%d"), tuya_buffer[13]); AddLog(LOG_LEVEL_DEBUG); - tuya_new_dim = round(serial_in_buffer[13] * (100. / 255.)); + tuya_new_dim = round(tuya_buffer[13] * (100. / 255.)); if((power) && (tuya_new_dim > 0) && (abs(tuya_new_dim - Settings.light_dimmer) > 2)) { snprintf_P(log_data, sizeof(log_data), PSTR("TYA: Send CMND_DIMMER=%d"), tuya_new_dim ); @@ -138,7 +142,7 @@ void TuyaPacketProcess() ExecuteCommand(scmnd, SRC_SWITCH); } } - else if (serial_in_byte_counter == 8 && serial_in_buffer[3] == 5 && serial_in_buffer[5] == 1 && serial_in_buffer[7] == 5 ) { // reset WiFi settings packet - to do: reset red MCU LED after WiFi is up + else if (tuya_byte_counter == 8 && tuya_buffer[3] == 5 && tuya_buffer[5] == 1 && tuya_buffer[7] == 5 ) { // reset WiFi settings packet - to do: reset red MCU LED after WiFi is up AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: WiFi Reset Rcvd")); @@ -149,16 +153,16 @@ void TuyaPacketProcess() void TuyaSerialInput() { - while (TuyaSerial && TuyaSerial->available()) { + while (TuyaSerial->available()) { yield(); - serial_in_byte = TuyaSerial->read(); + byte serial_in_byte = TuyaSerial->read(); - //snprintf_P(log_data, sizeof(log_data), PSTR("TYA: serial_in_byte %d, tuya_cmd_status %d, tuya_cmd_checksum %d, tuya_data_len %d, serial_in_byte_counter %d"), serial_in_byte, tuya_cmd_status, tuya_cmd_checksum, tuya_data_len, serial_in_byte_counter); + //snprintf_P(log_data, sizeof(log_data), PSTR("TYA: serial_in_byte %d, tuya_cmd_status %d, tuya_cmd_checksum %d, tuya_data_len %d, tuya_byte_counter %d"), serial_in_byte, tuya_cmd_status, tuya_cmd_checksum, tuya_data_len, tuya_byte_counter); //AddLog(LOG_LEVEL_DEBUG); if (serial_in_byte == 0x55) { // Start TUYA Packet tuya_cmd_status = 1; - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + tuya_buffer[tuya_byte_counter++] = serial_in_byte; tuya_cmd_checksum += serial_in_byte; } else if (tuya_cmd_status == 1 && serial_in_byte == 0xAA){ // Only packtes with header 0x55AA are valid @@ -166,40 +170,40 @@ void TuyaSerialInput() AddLog_P(LOG_LEVEL_DEBUG, PSTR("TYA: 0x55AA Packet Start")); - serial_in_byte_counter = 0; - serial_in_buffer[serial_in_byte_counter++] = 0x55; - serial_in_buffer[serial_in_byte_counter++] = 0xAA; + tuya_byte_counter = 0; + tuya_buffer[tuya_byte_counter++] = 0x55; + tuya_buffer[tuya_byte_counter++] = 0xAA; tuya_cmd_checksum = 0xFF; } else if (tuya_cmd_status == 2){ - if(serial_in_byte_counter == 5){ // Get length of data + if(tuya_byte_counter == 5){ // Get length of data tuya_cmd_status = 3; tuya_data_len = serial_in_byte; } tuya_cmd_checksum += serial_in_byte; - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + tuya_buffer[tuya_byte_counter++] = serial_in_byte; } - else if ((tuya_cmd_status == 3) && (serial_in_byte_counter == (6 + tuya_data_len)) && (tuya_cmd_checksum == serial_in_byte)){ // Compare checksum and process packet - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + else if ((tuya_cmd_status == 3) && (tuya_byte_counter == (6 + tuya_data_len)) && (tuya_cmd_checksum == serial_in_byte)){ // Compare checksum and process packet + tuya_buffer[tuya_byte_counter++] = serial_in_byte; snprintf_P(log_data, sizeof(log_data), PSTR("TYA: 0x55 Packet End: \"")); - for (int i = 0; i < serial_in_byte_counter; i++) { - snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, serial_in_buffer[i]); + for (int i = 0; i < tuya_byte_counter; i++) { + snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, tuya_buffer[i]); } snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data); AddLog(LOG_LEVEL_DEBUG); TuyaPacketProcess(); - serial_in_byte_counter = 0; + tuya_byte_counter = 0; tuya_cmd_status = 0; tuya_cmd_checksum = 0; tuya_data_len = 0; } // read additional packets from TUYA - else if(serial_in_byte_counter < INPUT_BUFFER_SIZE -1) { // add char to string if it still fits - serial_in_buffer[serial_in_byte_counter++] = serial_in_byte; + else if(tuya_byte_counter < TUYA_BUFFER_SIZE -1) { // add char to string if it still fits + tuya_buffer[tuya_byte_counter++] = serial_in_byte; tuya_cmd_checksum += serial_in_byte; } else { - serial_in_byte_counter = 0; + tuya_byte_counter = 0; tuya_cmd_status = 0; tuya_cmd_checksum = 0; tuya_data_len = 0; @@ -209,7 +213,10 @@ void TuyaSerialInput() boolean TuyaModuleSelected() { - baudrate = 9600; + if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { // fallback to hardware-serial if not explicitly selected + pin[GPIO_TUYA_RX] = 1; + pin[GPIO_TUYA_TX] = 3; + } light_type = LT_SERIAL; return true; } @@ -219,23 +226,14 @@ void TuyaInit() if (!Settings.param[P_TUYA_DIMMER_ID]) { Settings.param[P_TUYA_DIMMER_ID] = TUYA_DIMMER_ID; } - if (!(pin[GPIO_TUYA_RX] < 99) || !(pin[GPIO_TUYA_TX] < 99)) { // fallback to hardware-serial if not explicitly selected - pin[GPIO_TUYA_RX] = 1; - pin[GPIO_TUYA_TX] = 3; - } TuyaSerial = new TasmotaSerial(pin[GPIO_TUYA_RX], pin[GPIO_TUYA_TX], 1); - if (TuyaSerial->begin(baudrate)) { - if (TuyaSerial->hardwareSerial()) { - ClaimSerial(); - //Serial.setDebugOutput(false); - } - } + if (TuyaSerial->begin(9600)) { + if (TuyaSerial->hardwareSerial()) { ClaimSerial(); } + + // Get current status of MCU + snprintf_P(log_data, sizeof(log_data), "TYA: Request MCU state"); + AddLog(LOG_LEVEL_DEBUG); - // Get current status of MCU - snprintf_P(log_data, sizeof(log_data), "TYA: Request MCU state"); - AddLog(LOG_LEVEL_DEBUG); - - if(TuyaSerial){ TuyaSerial->write((uint8_t)0x55); // header 55AA TuyaSerial->write((uint8_t)0xAA); TuyaSerial->write((uint8_t)0x00); // version 00 @@ -282,7 +280,7 @@ boolean Xdrv16(byte function) TuyaInit(); break; case FUNC_LOOP: - TuyaSerialInput(); + if (TuyaSerial) { TuyaSerialInput(); } break; case FUNC_SET_DEVICE_POWER: result = TuyaSetPower(); From 4f59d102e7dff6f381a1517f6facfaf20dbfc285 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 27 Oct 2018 17:15:52 +0200 Subject: [PATCH 11/16] Add sonoff-basic.bin Add sonoff-basic.bin without most sensors --- platformio.ini | 15 +++++++ sonoff/_changelog.ino | 1 + sonoff/my_user_config.h | 6 +-- sonoff/sonoff_post.h | 99 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 109 insertions(+), 12 deletions(-) diff --git a/platformio.ini b/platformio.ini index 4f86fd3ac..e9ee43899 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,6 +13,7 @@ src_dir = sonoff ; *** Uncomment one of the lines below to build/upload only one environment env_default = sonoff ;env_default = sonoff-minimal +;env_default = sonoff-basic ;env_default = sonoff-classic ;env_default = sonoff-knx ;env_default = sonoff-sensors @@ -130,6 +131,20 @@ upload_resetmethod = ${common.upload_resetmethod} upload_speed = ${common.upload_speed} extra_scripts = ${common.extra_scripts} +[env:sonoff-basic] +platform = ${common.platform} +framework = ${common.framework} +board = ${common.board} +board_build.flash_mode = ${common.board_build.flash_mode} +board_build.f_cpu = ${common.board_build.f_cpu} +build_unflags = ${common.build_unflags} +build_flags = ${common.build_flags} -DUSE_BASIC +monitor_speed = ${common.monitor_speed} +upload_port = ${common.upload_port} +upload_resetmethod = ${common.upload_resetmethod} +upload_speed = ${common.upload_speed} +extra_scripts = ${common.extra_scripts} + [env:sonoff-classic] platform = ${common.platform} framework = ${common.framework} diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index c043fbe1f..b1417029e 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -5,6 +5,7 @@ * Initial release of RF transceiving using library RcSwitch (#2702) * Change default OTA Url to http://thehackbox.org/tasmota/release/sonoff.bin (#4170) * Add Tuya Software Serial to support additional Tuya configurations (#4178) + * Add sonoff-basic.bin without most sensors * * 6.2.1.18 20181019 * Add more API callbacks and document API.md diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index a086c6d62..81eb4e987 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -353,6 +353,8 @@ #define SDM630_SPEED 9600 // SDM630-Modbus RS485 serial speed (default: 9600 baud) //#define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop #define MP3_VOLUME 10 // Set the startup volume on init, the range can be 0..30(max) +#define USE_TUYA_DIMMER // Add support for Tuya Serial Dimmer + #define TUYA_DIMMER_ID 3 // Default dimmer Id // Power monitoring sensors ----------------------- #define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) @@ -379,9 +381,6 @@ #define USE_RF_FLASH // Add support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB (+3k code) -#define USE_TUYA_DIMMER // Add support for Tuya Serial Dimmer - #define TUYA_DIMMER_ID 3 // Default dimmer Id - #define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+2k code) #define USE_RC_SWITCH // Add support for RF transceiver using library RcSwitch (+2k7 code, 460 iram) @@ -399,6 +398,7 @@ \*********************************************************************************************/ //#define USE_CLASSIC // Create sonoff-classic with initial configuration tools WPS, SmartConfig and WifiManager +//#define USE_BASIC // Create sonoff-basic with no sensors //#define USE_SENSORS // Create sonoff-sensors with useful sensors enabled //#define USE_KNX_NO_EMULATION // Create sonoff-knx with KNX but without Emulation //#define USE_DISPLAYS // Create sonoff-display with display drivers enabled diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h index 13824a262..186d0a349 100755 --- a/sonoff/sonoff_post.h +++ b/sonoff/sonoff_post.h @@ -57,10 +57,7 @@ void KNX_CB_Action(message_t const &msg, void *arg); #ifdef USE_SENSORS -#ifdef USE_ADC_VCC -#undef USE_ADC_VCC -#endif -//#define USE_ADC_VCC // Display Vcc in Power status. Disable for use as Analog input on selected devices +#undef USE_ADC_VCC // Add Analog input on selected devices #define USE_DS18x20 // For more than one DS18x20 sensors with id sort, single scan and read retry (+1k3 code) //#define USE_DS18x20_LEGACY // For more than one DS18x20 sensors with dynamic scan using library OneWire (+1k5 code) #define USE_I2C // I2C using library wire (+10k code, 0k2 mem, 124 iram) @@ -80,7 +77,17 @@ void KNX_CB_Action(message_t const &msg, void *arg); #define USE_INA219 // Add I2C code for INA219 Low voltage and current sensor (+1k code) #define USE_MGS // Add I2C code for Xadow and Grove Mutichannel Gas sensor using library Multichannel_Gas_Sensor (+10k code) //#define USE_APDS9960 // Add I2C code for APDS9960 Proximity Sensor. Disables SHT and VEML6070 (+4k7 code) +//#define USE_MCP230xx // Enable MCP23008/MCP23017 - Must define I2C Address in #define USE_MCP230xx_ADDR below - range 0x20 - 0x27 (+4k7 code) +// #define USE_MCP230xx_ADDR 0x20 // Enable MCP23008/MCP23017 I2C Address to use (Must be within range 0x20 through 0x27 - set according to your wired setup) +// #define USE_MCP230xx_OUTPUT // Enable MCP23008/MCP23017 OUTPUT support through sensor29 commands (+1k5 code) +// #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 // Add I2C code for CCS811 sensor (+2k2 code) +//#define USE_MPU6050 // Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+2k6 code) +//#define USE_DS3231 // Enable DS3231 external RTC in case no Wifi is avaliable. See docs in the source file (+1k2 code) #define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code) #define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code) #ifndef CO2_LOW @@ -91,10 +98,17 @@ void KNX_CB_Action(message_t const &msg, void *arg); #endif #define USE_PMS5003 // Add support for PMS5003 and PMS7003 particle concentration sensor (+1k3 code) #define USE_NOVA_SDS // Add support for SDS011 and SDS021 particle concentration sensor (+0k7 code) -#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) #define USE_SERIAL_BRIDGE // Add support for software Serial Bridge (+0k8 code) #define USE_SDM120 // Add support for Eastron SDM120-Modbus energy meter (+1k7 code) #define USE_SDM630 // Add support for Eastron SDM630-Modbus energy meter (+2k code) +#define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop + #define MP3_VOLUME 10 // Set the startup volume on init, the range can be 0..30(max) +#define USE_TUYA_DIMMER // Add support for Tuya Serial Dimmer + #define TUYA_DIMMER_ID 3 // Default dimmer Id +#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code) +#define USE_PZEM_AC // Add support for PZEM014,016 Energy monitor (+1k1 code) +#define USE_PZEM_DC // Add support for PZEM003,017 Energy monitor (+1k1 code) +#define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code) #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram) #define USE_IR_HVAC // Support for HVAC system using IR (+2k code) #define USE_IR_RECEIVE // Support for IR receiver (+5k5 code, 264 iram) @@ -105,6 +119,12 @@ void KNX_CB_Action(message_t const &msg, void *arg); // #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow #define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code, 252 iram (non 2.3.0)) #define USE_SR04 // Add support for HC-SR04 ultrasonic devices (+1k code) +#define USE_TM1638 // Add support for TM1638 switches copying Switch1 .. Switch8 (+1k code) +#define USE_HX711 // Add support for HX711 load cell (+1k5 code) +//#define USE_HX711_GUI // Add optional web GUI to HX711 as scale (+1k8 code) +#define USE_RF_FLASH // Add support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB (+3k code) +#define USE_TX20_WIND_SENSOR // Add support for La Crosse TX20 anemometer (+2k code) +#define USE_RC_SWITCH // Add support for RF transceiver using library RcSwitch (+2k7 code, 460 iram) #endif // USE_SENSORS /*********************************************************************************************\ @@ -141,6 +161,8 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge #undef USE_SDM120 // Disable support for Eastron SDM120-Modbus energy meter #undef USE_SDM630 // Disable support for Eastron SDM630-Modbus energy meter +#undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop +#undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer #undef USE_IR_REMOTE // Disable IR remote commands using library IRremoteESP8266 and ArduinoJson #undef USE_IR_RECEIVE // Disable support for IR receiver #undef USE_ARILUX_RF // Disable support for Arilux RF remote controller @@ -148,7 +170,6 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_TM1638 // Disable support for TM1638 switches copying Switch1 .. Switch8 #undef USE_HX711 // Disable support for HX711 load cell #undef USE_RF_FLASH // Disable support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB -#undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer #undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer #undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch #undef DEBUG_THEO // Disable debug code @@ -201,6 +222,62 @@ void KNX_CB_Action(message_t const &msg, void *arg); #define USE_DS18B20 // Default DS18B20 sensor needs no external library #endif +/*********************************************************************************************\ + * [sonoff-basic.bin] + * Provide an image without sensors +\*********************************************************************************************/ + +#ifdef USE_BASIC + +//#undef USE_ENERGY_SENSOR // Disable energy sensors +#undef USE_ARDUINO_OTA // Disable support for Arduino OTA +#undef USE_WPS // Disable support for WPS as initial wifi configuration tool +#undef USE_SMARTCONFIG // Disable support for Wifi SmartConfig as initial wifi configuration tool +#undef USE_DOMOTICZ // Disable Domoticz +#undef USE_HOME_ASSISTANT // Disable Home Assistant +#undef USE_MQTT_TLS // Disable TLS support won't work as the MQTTHost is not set +#undef USE_KNX // Disable KNX IP Protocol Support +//#undef USE_WEBSERVER // Disable Webserver +//#undef USE_EMULATION // Disable Wemo or Hue emulation +#undef USE_CUSTOM // Disable Custom features +#undef USE_DISCOVERY // Disable Discovery services for both MQTT and web server +//#undef USE_TIMERS // Disable support for up to 16 timers +//#undef USE_TIMERS_WEB // Disable support for timer webpage +//#undef USE_SUNRISE // Disable support for Sunrise and sunset tools +//#undef USE_RULES // Disable support for rules +#undef USE_DHT // Disable internal DHT sensor +#undef USE_DS18x20 // Disable DS18x20 sensor +#undef USE_DS18x20_LEGACY // Disable DS18x20 sensor +#undef USE_DS18B20 // Disable internal DS18B20 sensor +#undef USE_I2C // Disable all I2C sensors and devices +#undef USE_SPI // Disable all SPI devices +#undef USE_DISPLAY // Disable Display support +#undef USE_MHZ19 // Disable support for MH-Z19 CO2 sensor +#undef USE_SENSEAIR // Disable support for SenseAir K30, K70 and S8 CO2 sensor +#undef USE_PMS5003 // Disable support for PMS5003 and PMS7003 particle concentration sensor +#undef USE_NOVA_SDS // Disable support for SDS011 and SDS021 particle concentration sensor +#undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge +#undef USE_SDM120 // Disable support for Eastron SDM120-Modbus energy meter +#undef USE_SDM630 // Disable support for Eastron SDM630-Modbus energy meter +#undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop +//#undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer +#undef USE_PZEM004T // Disable PZEM004T energy sensor +#undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor +#undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor +//#undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 +#undef USE_IR_REMOTE // Disable IR driver +#undef USE_WS2812 // Disable WS2812 Led string +#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller +#undef USE_SR04 // Disable support for for HC-SR04 ultrasonic devices +#undef USE_TM1638 // Disable support for TM1638 switches copying Switch1 .. Switch8 +#undef USE_HX711 // Disable support for HX711 load cell +#undef USE_RF_FLASH // Disable support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB +#undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer +#undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch +#undef DEBUG_THEO // Disable debug code +#undef USE_DEBUG_DRIVER // Disable debug code +#endif // USE_BASIC + /*********************************************************************************************\ * [sonoff-minimal.bin] * Provide the smallest image possible while still enabling a webserver for intermediate image load @@ -217,9 +294,9 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_MQTT_TLS // Disable TLS support won't work as the MQTTHost is not set #undef USE_KNX // Disable KNX IP Protocol Support //#undef USE_WEBSERVER // Disable Webserver +#undef USE_EMULATION // Disable Wemo or Hue emulation #undef USE_CUSTOM // Disable Custom features #undef USE_DISCOVERY // Disable Discovery services for both MQTT and web server -#undef USE_EMULATION // Disable Wemo or Hue emulation #undef USE_TIMERS // Disable support for up to 16 timers #undef USE_TIMERS_WEB // Disable support for timer webpage #undef USE_SUNRISE // Disable support for Sunrise and sunset tools @@ -235,10 +312,15 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_SENSEAIR // Disable support for SenseAir K30, K70 and S8 CO2 sensor #undef USE_PMS5003 // Disable support for PMS5003 and PMS7003 particle concentration sensor #undef USE_NOVA_SDS // Disable support for SDS011 and SDS021 particle concentration sensor -#undef USE_PZEM004T // Disable PZEM004T energy sensor #undef USE_SERIAL_BRIDGE // Disable support for software Serial Bridge #undef USE_SDM120 // Disable support for Eastron SDM120-Modbus energy meter #undef USE_SDM630 // Disable support for Eastron SDM630-Modbus energy meter +#undef USE_MP3_PLAYER // Disable DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop +#undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer +#undef USE_PZEM004T // Disable PZEM004T energy sensor +#undef USE_PZEM_AC // Disable PZEM014,016 Energy monitor +#undef USE_PZEM_DC // Disable PZEM003,017 Energy monitor +#undef USE_MCP39F501 // Disable MCP39F501 Energy monitor as used in Shelly 2 #undef USE_IR_REMOTE // Disable IR driver #undef USE_WS2812 // Disable WS2812 Led string #undef USE_ARILUX_RF // Disable support for Arilux RF remote controller @@ -246,7 +328,6 @@ void KNX_CB_Action(message_t const &msg, void *arg); #undef USE_TM1638 // Disable support for TM1638 switches copying Switch1 .. Switch8 #undef USE_HX711 // Disable support for HX711 load cell #undef USE_RF_FLASH // Disable support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB -#undef USE_TUYA_DIMMER // Disable support for Tuya Serial Dimmer #undef USE_TX20_WIND_SENSOR // Disable support for La Crosse TX20 anemometer #undef USE_RC_SWITCH // Disable support for RF transceiver using library RcSwitch #undef DEBUG_THEO // Disable debug code From b59d63a07a8c64788d887d55c939eba9b3f48013 Mon Sep 17 00:00:00 2001 From: Jason2866 Date: Sun, 28 Oct 2018 11:17:41 +0100 Subject: [PATCH 12/16] Reverting Alexa fix General Alexa for all cores doesnt work. Reverting fix. This fix does only work on Stage core 2.5.0. Awaiting new general fix from @ascillato soon --- sonoff/xplg_wemohue.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/xplg_wemohue.ino b/sonoff/xplg_wemohue.ino index 2f70174f0..f8eff5ddc 100644 --- a/sonoff/xplg_wemohue.ino +++ b/sonoff/xplg_wemohue.ino @@ -664,7 +664,7 @@ void HueLights(String *path) response = "["; StaticJsonBuffer<400> jsonBuffer; - JsonObject &hue_json = jsonBuffer.parseObject(WebServer->arg("1")); + JsonObject &hue_json = jsonBuffer.parseObject(WebServer->arg((WebServer->args())-1)); if (hue_json.containsKey("on")) { response += FPSTR(HUE_LIGHT_RESPONSE_JSON); From a9d87de8e8c807a0b1dc753d4cbaf51938d15f45 Mon Sep 17 00:00:00 2001 From: Jason2866 Date: Sun, 28 Oct 2018 12:13:36 +0100 Subject: [PATCH 13/16] Add tested Beta Arduino Core 2.5.0 Add tested and known working Beta Arduino Core 2.5.0 for Tasmota. Unchanged Fork from Original https://github.com/esp8266/Arduino New commits to Beta core 2.5.0 will only be added if known to be working with Tasmota --- platformio.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platformio.ini b/platformio.ini index e9ee43899..e685cf34f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -46,6 +46,8 @@ platform = espressif8266@1.5.0 ;platform = espressif8266@1.7.3 ; *** Esp8266 core for Arduino version 2.4.2 ;platform = espressif8266@1.8.0 +; *** Esp8266 core for Arduino version Core 2.5.0 beta tested for Tasmota +;platform = https://github.com/Jason2866/platform-espressif8266.git#Tasmota ; *** Esp8266 core for Arduino version latest beta ;platform = https://github.com/platformio/platform-espressif8266.git#feature/stage ; *** Esp8266 core for Arduino current version (located in %USERPROFILE%\.platformio\platforms\espressif8266) From 259c638cb8d961fc16122789f1d796c0fde44ac7 Mon Sep 17 00:00:00 2001 From: Jason2866 Date: Sun, 28 Oct 2018 14:14:10 +0100 Subject: [PATCH 14/16] Update en-GB.h --- sonoff/language/en-GB.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index 925080d3d..48641d0ff 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "Hr" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kOhm" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" From 12f17cc86a877e2edfdaa909ca04e217d832d77c Mon Sep 17 00:00:00 2001 From: Jason2866 Date: Sun, 28 Oct 2018 14:17:01 +0100 Subject: [PATCH 15/16] Update de-DE.h --- sonoff/language/de-DE.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 6a783a45b..0c2ab9f6a 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "h" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kOhm" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" From 432c4df27aee16c1477711a819c8342dc02e5b62 Mon Sep 17 00:00:00 2001 From: Jason2866 Date: Sun, 28 Oct 2018 14:24:20 +0100 Subject: [PATCH 16/16] kmph to km/h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "SI, and hence the use of "km/h" (or "km h−1" or "km·h−1") has now been adopted around the world --- sonoff/language/bg-BG.h | 2 +- sonoff/language/el-GR.h | 3 ++- sonoff/language/es-AR.h | 2 +- sonoff/language/fr-FR.h | 2 +- sonoff/language/he-HE.h | 2 +- sonoff/language/hu-HU.h | 2 +- sonoff/language/it-IT.h | 2 +- sonoff/language/nl-NL.h | 2 +- sonoff/language/pl-PL.h | 2 +- sonoff/language/pt-BR.h | 2 +- sonoff/language/pt-PT.h | 2 +- sonoff/language/ru-RU.h | 2 +- sonoff/language/tr-TR.h | 2 +- sonoff/language/uk-UK.h | 2 +- sonoff/language/zh-CN.h | 2 +- sonoff/language/zh-TW.h | 2 +- 16 files changed, 17 insertions(+), 16 deletions(-) diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h index 7e096f673..d91c66bda 100644 --- a/sonoff/language/bg-BG.h +++ b/sonoff/language/bg-BG.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "h" #define D_UNIT_KILOGRAM "kg" #define D_UNIT_INCREMENTS "inc" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" diff --git a/sonoff/language/el-GR.h b/sonoff/language/el-GR.h index 937464699..c5a3db324 100644 --- a/sonoff/language/el-GR.h +++ b/sonoff/language/el-GR.h @@ -1,3 +1,4 @@ + /* el-GR.h - localization for Greek - Greece for Sonoff-Tasmota @@ -537,7 +538,7 @@ #define D_UNIT_HOUR "Hr" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kOhm" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-AR.h index d78c807da..a5d044985 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-AR.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "Hr" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kOhm" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index bb32c2d2e..0e37d9990 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "h" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kΩ" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h index d1aee3423..355fe8231 100644 --- a/sonoff/language/he-HE.h +++ b/sonoff/language/he-HE.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "Hr" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kOhm" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h index 91b005e39..cbe52378d 100644 --- a/sonoff/language/hu-HU.h +++ b/sonoff/language/hu-HU.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "ó" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kOhm" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index 2ed03ca35..8449d250d 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "Hr" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kOhm" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index ca7be61af..afaf9d08a 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "h" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kOhm" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index 3fd26f29b..686f5cc76 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "Godz" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kOhm" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h index dceadf085..ef5104fdb 100644 --- a/sonoff/language/pt-BR.h +++ b/sonoff/language/pt-BR.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "H" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kOhm" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index a7b9236ae..97750f38c 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "Hr" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kOhm" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index fc2dc2892..28aa1d7f2 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "Ч" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "кОм" #define D_UNIT_KILOWATTHOUR "кВт" #define D_UNIT_LUX "лк" diff --git a/sonoff/language/tr-TR.h b/sonoff/language/tr-TR.h index ea3f10ce6..0588b5175 100755 --- a/sonoff/language/tr-TR.h +++ b/sonoff/language/tr-TR.h @@ -536,7 +536,7 @@ #define D_UNIT_HOUR "Hr" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "kOhm" #define D_UNIT_KILOWATTHOUR "kWh" #define D_UNIT_LUX "lx" diff --git a/sonoff/language/uk-UK.h b/sonoff/language/uk-UK.h index cf9d8bcf3..fc32479fc 100644 --- a/sonoff/language/uk-UK.h +++ b/sonoff/language/uk-UK.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "Г" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "кОм" #define D_UNIT_KILOWATTHOUR "кВт" #define D_UNIT_LUX "лк" diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index 15708dded..f4a19e324 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -536,7 +536,7 @@ #define D_UNIT_HOUR "时" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "千欧" #define D_UNIT_KILOWATTHOUR "千瓦时" #define D_UNIT_LUX "勒克斯" diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h index 75c682396..61fa90d4d 100644 --- a/sonoff/language/zh-TW.h +++ b/sonoff/language/zh-TW.h @@ -537,7 +537,7 @@ #define D_UNIT_HOUR "時" #define D_UNIT_INCREMENTS "inc" #define D_UNIT_KILOGRAM "kg" -#define D_UNIT_KILOMETER_PER_HOUR "kmph" // or "km/h" +#define D_UNIT_KILOMETER_PER_HOUR "km/h" // or "km/h" #define D_UNIT_KILOOHM "千歐" #define D_UNIT_KILOWATTHOUR "千瓦時" #define D_UNIT_LUX "勒克斯"