diff --git a/tasmota/berry/include/be_gpio_defines.h b/tasmota/berry/include/be_gpio_defines.h index ff68ac354..f037d9480 100644 --- a/tasmota/berry/include/be_gpio_defines.h +++ b/tasmota/berry/include/be_gpio_defines.h @@ -294,6 +294,8 @@ const be_const_member_t lv_gpio_constants[] = { { "TM1638STB", (int32_t) GPIO_TM1638STB }, { "TUYA_RX", (int32_t) GPIO_TUYA_RX }, { "TUYA_TX", (int32_t) GPIO_TUYA_TX }, + { "TUYAMCUBR_RX", (int32_t) GPIO_TUYAMCUBR_RX }, + { "TUYAMCUBR_TX", (int32_t) GPIO_TUYAMCUBR_TX }, { "TX2X_TXD_BLACK", (int32_t) GPIO_TX2X_TXD_BLACK }, { "TXD", (int32_t) GPIO_TXD }, { "VINDRIKTNING_RX", (int32_t) GPIO_VINDRIKTNING_RX }, diff --git a/tasmota/include/tasmota_configurations.h b/tasmota/include/tasmota_configurations.h index b787e1dcd..1ed060c54 100644 --- a/tasmota/include/tasmota_configurations.h +++ b/tasmota/include/tasmota_configurations.h @@ -41,6 +41,7 @@ #ifndef TUYA_DIMMER_ID #define TUYA_DIMMER_ID 0 // Default dimmer Id #endif +#define USE_TUYAMCUBR #undef USE_ARMTRONIX_DIMMERS // Disable support for Armtronix Dimmers (+1k4 code) #undef USE_PS_16_DZ // Disable support for PS-16-DZ Dimmer (+2k code) #undef USE_SONOFF_IFAN // Disable support for Sonoff iFan02 and iFan03 (+2k code) diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index 60a85240a..047e6766a 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -204,6 +204,7 @@ enum UserSelectablePins { GPIO_LD2410_TX, GPIO_LD2410_RX, // HLK-LD2410 GPIO_MBR_TX_ENA, GPIO_NRG_MBS_TX_ENA, // Modbus Bridge Serial Transmit Enable GPIO_ME007_TRIG, GPIO_ME007_RX, // ME007 Serial/Trigger interface + GPIO_TUYAMCUBR_TX, GPIO_TUYAMCUBR_RX, // TuyaMCU Bridge GPIO_SENSOR_END }; // Error as warning to rethink GPIO usage with max 2045 @@ -455,6 +456,7 @@ const char kSensorNames[] PROGMEM = D_SENSOR_LD2410_TX "|" D_SENSOR_LD2410_RX "|" D_SENSOR_MBR_TX_ENA "|" D_SENSOR_NRG_MBS_TX_ENA "|" D_SENSOR_ME007_TRIG "|" D_SENSOR_ME007_RX "|" + D_SENSOR_TUYAMCUBR_TX "|" D_SENSOR_TUYAMCUBR_RX "|" ; const char kSensorNamesFixed[] PROGMEM = @@ -1056,6 +1058,10 @@ const uint16_t kGpioNiceList[] PROGMEM = { AGPIO(GPIO_MIEL_HVAC_TX), // Mitsubishi Electric HVAC TX pin AGPIO(GPIO_MIEL_HVAC_RX), // Mitsubishi Electric HVAC RX pin #endif +#ifdef USE_TUYAMCUBR + AGPIO(GPIO_TUYAMCUBR_TX), + AGPIO(GPIO_TUYAMCUBR_RX), +#endif #ifdef USE_WIEGAND AGPIO(GPIO_WIEGAND_D0), // Date line D0 of Wiegand devices AGPIO(GPIO_WIEGAND_D1), // Date line D1 of Wiegand devices diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h index 90d1cc897..7d1c464f1 100644 --- a/tasmota/language/af_AF.h +++ b/tasmota/language/af_AF.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h index eacd7b06b..dfb0a19f2 100644 --- a/tasmota/language/bg_BG.h +++ b/tasmota/language/bg_BG.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Дебитомер" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "А" diff --git a/tasmota/language/ca_AD.h b/tasmota/language/ca_AD.h index cd7303851..3a3fca3fd 100644 --- a/tasmota/language/ca_AD.h +++ b/tasmota/language/ca_AD.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Cabal" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h index 2dda6f462..3aa103e51 100644 --- a/tasmota/language/cs_CZ.h +++ b/tasmota/language/cs_CZ.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h index a655df3ed..ed4d18686 100644 --- a/tasmota/language/de_DE.h +++ b/tasmota/language/de_DE.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h index 25a808e28..9b885d832 100644 --- a/tasmota/language/el_GR.h +++ b/tasmota/language/el_GR.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h index 805cefd30..8bcdab91d 100644 --- a/tasmota/language/en_GB.h +++ b/tasmota/language/en_GB.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h index 68d8da907..d5b64734e 100644 --- a/tasmota/language/es_ES.h +++ b/tasmota/language/es_ES.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h index 917389ff3..c1d2b1c72 100644 --- a/tasmota/language/fr_FR.h +++ b/tasmota/language/fr_FR.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/fy_NL.h b/tasmota/language/fy_NL.h index 70a30215b..cc4329c89 100644 --- a/tasmota/language/fy_NL.h +++ b/tasmota/language/fy_NL.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h index 33360d150..de30f6352 100644 --- a/tasmota/language/he_HE.h +++ b/tasmota/language/he_HE.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h index 52f536ec4..00da79687 100644 --- a/tasmota/language/hu_HU.h +++ b/tasmota/language/hu_HU.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index a962827bd..19a763846 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Portata" #define D_SENSOR_ME007_TRIG "ME007 - Tri" #define D_SENSOR_ME007_RX "ME007 - RX" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h index d09b81e03..dcd06089e 100644 --- a/tasmota/language/ko_KO.h +++ b/tasmota/language/ko_KO.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h index 7d0624efb..ea9130f0f 100644 --- a/tasmota/language/nl_NL.h +++ b/tasmota/language/nl_NL.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h index 10c0b3b68..0e0415fb0 100644 --- a/tasmota/language/pl_PL.h +++ b/tasmota/language/pl_PL.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h index 1fce258ee..c7ee07fe3 100644 --- a/tasmota/language/pt_BR.h +++ b/tasmota/language/pt_BR.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h index af1763dc8..74ae4e72e 100644 --- a/tasmota/language/pt_PT.h +++ b/tasmota/language/pt_PT.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h index 0dd99d363..1e2f6f992 100644 --- a/tasmota/language/ro_RO.h +++ b/tasmota/language/ro_RO.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h index 8de5385e5..893693054 100644 --- a/tasmota/language/ru_RU.h +++ b/tasmota/language/ru_RU.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "А" diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h index 697020c42..9ede0cf21 100644 --- a/tasmota/language/sk_SK.h +++ b/tasmota/language/sk_SK.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h index d0607da30..1145c3f2c 100644 --- a/tasmota/language/sv_SE.h +++ b/tasmota/language/sv_SE.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h index dcb243c02..b43eaf75c 100644 --- a/tasmota/language/tr_TR.h +++ b/tasmota/language/tr_TR.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h index cc5c3de2f..1c72ce490 100644 --- a/tasmota/language/uk_UA.h +++ b/tasmota/language/uk_UA.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "А" diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h index ef0035a26..1ca96fd17 100644 --- a/tasmota/language/vi_VN.h +++ b/tasmota/language/vi_VN.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h index b3f8cf5fe..5690e5018 100644 --- a/tasmota/language/zh_CN.h +++ b/tasmota/language/zh_CN.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "A" diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h index a4fd10115..9f9e1959a 100644 --- a/tasmota/language/zh_TW.h +++ b/tasmota/language/zh_TW.h @@ -917,6 +917,8 @@ #define D_SENSOR_FLOWRATEMETER "Flowrate" #define D_SENSOR_ME007_TRIG "ME007 Tri" #define D_SENSOR_ME007_RX "ME007 Rx" +#define D_SENSOR_TUYAMCUBR_TX "TuyaMCUBr Tx" +#define D_SENSOR_TUYAMCUBR_RX "TuyaMCUBr Rx" // Units #define D_UNIT_AMPERE "安培" diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 2c0e749f2..7933a9343 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -533,6 +533,7 @@ #define USE_TUYA_MCU // Add support for Tuya Serial MCU #define TUYA_DIMMER_ID 0 // Default dimmer Id #define USE_TUYA_TIME // Add support for Set Time in Tuya MCU +#define USE_TUYAMCUBR // Add support for TuyaMCU Bridge #define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code) #define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer (+2k code) #define USE_SONOFF_IFAN // Add support for Sonoff iFan02 and iFan03 (+2k code) @@ -822,6 +823,7 @@ #define USE_TASMOTA_CLIENT_SERIAL_SPEED 57600 // Depends on the sketch that is running on the Uno/Pro Mini //#define USE_OPENTHERM // Add support for OpenTherm (+15k code) //#define USE_MIEL_HVAC // Add support for Mitsubishi Electric HVAC serial interface (+5k code) +//#define USE_TUYAMCUBR // Add support for TuyaMCU Bridge //#define USE_PROJECTOR_CTRL // Add support for LCD/DLP Projector serial control interface (+2k code) // #define USE_PROJECTOR_CTRL_NEC // Use codes for NEC // #define USE_PROJECTOR_CTRL_OPTOMA // Use codes for OPTOMA diff --git a/tasmota/tasmota_support/support_features.ino b/tasmota/tasmota_support/support_features.ino index 78a1d7d74..8f52b111e 100644 --- a/tasmota/tasmota_support/support_features.ino +++ b/tasmota/tasmota_support/support_features.ino @@ -867,7 +867,10 @@ void ResponseAppendFeatures(void) #if defined(USE_I2C) && defined(USE_PCA9632) feature9 |= 0x00002000; #endif -// feature9 |= 0x00004000; +#ifdef USE_TUYAMCUBR + feature9 |= 0x00004000; // xdrv_65_tuyamcubr.ino +#endif + // feature9 |= 0x00008000; // feature9 |= 0x00010000; diff --git a/tasmota/tasmota_xdrv_driver/.xdrv_65_tuyamcubr.ino.swp b/tasmota/tasmota_xdrv_driver/.xdrv_65_tuyamcubr.ino.swp new file mode 100644 index 000000000..e97c20288 Binary files /dev/null and b/tasmota/tasmota_xdrv_driver/.xdrv_65_tuyamcubr.ino.swp differ diff --git a/tasmota/tasmota_xdrv_driver/xdrv_44_miel_hvac.ino b/tasmota/tasmota_xdrv_driver/xdrv_44_miel_hvac.ino index 60ea83e5a..65ab1ecdd 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_44_miel_hvac.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_44_miel_hvac.ino @@ -24,10 +24,14 @@ #define XDRV_44 44 +#ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif +#ifndef CTASSERT #define CTASSERT(x) extern char _ctassert[(x) ? 1 : -1 ] \ __attribute__((__unused__)) +#endif #define MIEL_HVAC_LOGNAME "MiElHVAC" diff --git a/tasmota/tasmota_xdrv_driver/xdrv_65_tuyamcubr.ino b/tasmota/tasmota_xdrv_driver/xdrv_65_tuyamcubr.ino new file mode 100644 index 000000000..c51c3daac --- /dev/null +++ b/tasmota/tasmota_xdrv_driver/xdrv_65_tuyamcubr.ino @@ -0,0 +1,980 @@ +/* + * xdrv_65_tuyamcubr.ino - TuyaMCU Bridge support for Tasmota + */ + +/* + * Copyright (C) 2023 David Gwynne + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef USE_TUYAMCUBR + +/* + * Tuya MCU Bridge + */ + +/* + * TODO: + * + * - handling wifi reset requests from the MCU + * - low power stuff? + * - support for (re)sending status updates and device info queries + * - supporting the raw and string Dp types + * - restarting the tuya mcu state machine? + * - restarting the rx state machine when no bytes are rxed for a while + * - time sync + */ + +#define XDRV_65 65 + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +#ifndef CTASSERT +#define CTASSERT(x) extern char _ctassert[(x) ? 1 : -1 ] \ + __attribute__((__unused__)) +#endif + +#define TUYAMCUBR_LOGNAME "TYB" +#define TUYAMCUBR_FMT(_fmt) PSTR(TUYAMCUBR_LOGNAME ": " _fmt) + +#define D_CMND_TUYAMCUBR_PREFIX "TuyaMCU" + +#define D_CMND_TUYAMCUBR_DATA_RAW "Raw" +#define D_CMND_TUYAMCUBR_DATA_BOOL "Bool" +#define D_CMND_TUYAMCUBR_DATA_VALUE "Value" +#define D_CMND_TUYAMCUBR_DATA_STRING "String" +#define D_CMND_TUYAMCUBR_DATA_ENUM "Enum" + +#include + +struct tuyamcubr_header { + uint8_t header[2]; +#define TUYAMCUBR_H_ONE 0x55 +#define TUYAMCUBR_H_TWO 0xaa + uint8_t version; + uint8_t command; + uint16_t datalen; +}; + +CTASSERT(sizeof(struct tuyamcubr_header) == 6); + +#define TUYAMCUBR_CMD_HEARTBEAT 0x00 +#define TUYAMCUBR_CMD_PRODUCT 0x01 +#define TUYAMCUBR_CMD_MODE 0x02 +#define TUYAMCUBR_CMD_WIFI_STATE 0x03 +#define TUYAMCUBR_CMD_WIFI_RESET 0x04 +#define TUYAMCUBR_CMD_WIFI_SELECT 0x05 +#define TUYAMCUBR_CMD_SET_DP 0x06 +#define TUYAMCUBR_CMD_STATE 0x07 +#define TUYAMCUBR_CMD_QUERY_STATE 0x08 +#define TUYAMCUBR_CMD_INIT_UPGRADE 0x0a +#define TUYAMCUBR_CMD_UPGRADE_PKG 0x0b +#define TUYAMCUBR_CMD_SET_TIME 0x1c + +/* wifi state */ + +#define TUYAMCUBR_NETWORK_STATUS_1 0x00 /* pairing in EZ mode */ +#define TUYAMCUBR_NETWORK_STATUS_2 0x01 /* pairing in AP mode */ +#define TUYAMCUBR_NETWORK_STATUS_3 0x02 /* WiFi */ +#define TUYAMCUBR_NETWORK_STATUS_4 0x03 /* WiFi + router */ +#define TUYAMCUBR_NETWORK_STATUS_5 0x04 /* WiFi + router + cloud*/ +#define TUYAMCUBR_NETWORK_STATUS_6 0x05 /* low power mode */ +#define TUYAMCUBR_NETWORK_STATUS_7 0x06 /* pairing in EZ+AP mode */ + +/* set dp */ + +struct tuyamcubr_data_header { + uint8_t dpid; + uint8_t type; + uint16_t len; + + /* followed by len bytes */ +}; + +CTASSERT(sizeof(struct tuyamcubr_data_header) == 4); + +#define TUYAMCUBR_DATA_TYPE_RAW 0x00 +#define TUYAMCUBR_DATA_TYPE_BOOL 0x01 +#define TUYAMCUBR_DATA_TYPE_VALUE 0x02 +#define TUYAMCUBR_DATA_TYPE_STRING 0x03 +#define TUYAMCUBR_DATA_TYPE_ENUM 0x04 + +struct tuyamcubr_data_type { + const char *t_name; + int t_len; + uint32_t t_max; + uint32_t (*t_rd)(const uint8_t *); + void (*t_wr)(uint8_t *, uint32_t); +}; + +static uint32_t +tuyamcubr_rd_u8(const uint8_t *b) +{ + return (*b); +} + +static void +tuyamcubr_wr_u8(uint8_t *b, uint32_t v) +{ + *b = v; +} + +static uint32_t +tuyamcubr_rd_u32(const uint8_t *b) +{ + uint32_t be32; + memcpy(&be32, b, sizeof(be32)); + return (ntohl(be32)); +} + +static void +tuyamcubr_wr_u32(uint8_t *b, uint32_t v) +{ + uint32_t be32 = htonl(v); + memcpy(b, &be32, sizeof(be32)); +} + +static const struct tuyamcubr_data_type tuyamcubr_data_types[] = { + [TUYAMCUBR_DATA_TYPE_RAW] = { + .t_name = D_CMND_TUYAMCUBR_DATA_RAW, + .t_len = -1, + }, + [TUYAMCUBR_DATA_TYPE_BOOL] = { + .t_name = D_CMND_TUYAMCUBR_DATA_BOOL, + .t_len = 1, + .t_max = 1, + .t_rd = tuyamcubr_rd_u8, + .t_wr = tuyamcubr_wr_u8, + }, + [TUYAMCUBR_DATA_TYPE_VALUE] = { + .t_name = D_CMND_TUYAMCUBR_DATA_VALUE, + .t_len = sizeof(uint32_t), + .t_max = 0xffffffff, + .t_rd = tuyamcubr_rd_u32, + .t_wr = tuyamcubr_wr_u32, + }, + [TUYAMCUBR_DATA_TYPE_STRING] = { + .t_name = D_CMND_TUYAMCUBR_DATA_STRING, + .t_len = -1, + }, + [TUYAMCUBR_DATA_TYPE_ENUM] = { + .t_name = D_CMND_TUYAMCUBR_DATA_ENUM, + .t_len = 1, + .t_max = 0xff, + .t_rd = tuyamcubr_rd_u8, + .t_wr = tuyamcubr_wr_u8, + }, +}; + +static inline const struct tuyamcubr_data_type * +tuyamcubr_find_data_type(uint8_t type) +{ + const struct tuyamcubr_data_type *dt; + + if (type > nitems(tuyamcubr_data_types)) + return (NULL); + + dt = &tuyamcubr_data_types[type]; + if (dt->t_name == NULL) + return (NULL); + + return (dt); +} + +static inline uint8_t +tuyamcubr_cksum_fini(uint8_t sum) +{ + /* + * "Start from the header, add up all the bytes, and then divide + * the sum by 256 to get the remainder." + * + * If we accumulate bytes in a uint8_t, we get this for free. + */ + + return (sum); +} + +enum tuyamcubr_parser_state { + TUYAMCUBR_P_START, + TUYAMCUBR_P_HEADER, + TUYAMCUBR_P_VERSION, + TUYAMCUBR_P_COMMAND, + TUYAMCUBR_P_LEN1, + TUYAMCUBR_P_LEN2, + TUYAMCUBR_P_DATA, + TUYAMCUBR_P_CKSUM, + + TUYAMCUBR_P_SKIP, + TUYAMCUBR_P_SKIP_CKSUM, +}; + +//#ifdef ESP8266 +//#define TUYAMCUBR_BUFLEN 256 +//#else +#define TUYAMCUBR_BUFLEN 1024 +//#endif + +struct tuyamcubr_parser { + enum tuyamcubr_parser_state p_state; + unsigned int p_deadline; + + uint8_t p_version; + uint8_t p_command; + uint8_t p_sum; + + uint16_t p_len; + uint8_t p_off; + uint8_t p_data[TUYAMCUBR_BUFLEN]; +}; + +struct tuyamcubr_dp { + STAILQ_ENTRY(tuyamcubr_dp) dp_entry; + uint8_t dp_id; + uint8_t dp_type; + + uint32_t dp_value; +}; +STAILQ_HEAD(tuyamcubr_dps, tuyamcubr_dp); + +enum tuyamcubr_state { + TUYAMCUBR_S_START, + TUYAMCUBR_S_PROD_INFO, + TUYAMCUBR_S_MODE, + TUYAMCUBR_S_NET_STATUS, + TUYAMCUBR_S_RUNNING, +}; + +struct tuyamcubr_softc { + TasmotaSerial *sc_serial; + struct tuyamcubr_parser sc_parser; + + enum tuyamcubr_state sc_state; + unsigned int sc_deadline; + unsigned int sc_waiting; + uint8_t sc_network_status; + + unsigned int sc_clock; + struct tuyamcubr_dps sc_dps; +}; + +static struct tuyamcubr_softc *tuyamcubr_sc = nullptr; + +struct tuyamcubr_recv_command { + uint8_t r_command; + void (*r_func)(struct tuyamcubr_softc *, uint8_t, + const uint8_t *, size_t); +}; + +static void tuyamcubr_recv_heartbeat(struct tuyamcubr_softc *, uint8_t, + const uint8_t *, size_t); +static void tuyamcubr_recv_product_info(struct tuyamcubr_softc *, uint8_t, + const uint8_t *, size_t); +static void tuyamcubr_recv_mode(struct tuyamcubr_softc *, uint8_t, + const uint8_t *, size_t); +static void tuyamcubr_recv_net_status(struct tuyamcubr_softc *, uint8_t, + const uint8_t *, size_t); +static void tuyamcubr_recv_status(struct tuyamcubr_softc *, uint8_t, + const uint8_t *, size_t); + +static const struct tuyamcubr_recv_command tuyamcubr_recv_commands[] = { + { TUYAMCUBR_CMD_HEARTBEAT, tuyamcubr_recv_heartbeat }, + { TUYAMCUBR_CMD_PRODUCT, tuyamcubr_recv_product_info }, + { TUYAMCUBR_CMD_MODE, tuyamcubr_recv_mode }, + { TUYAMCUBR_CMD_WIFI_STATE, tuyamcubr_recv_net_status }, + { TUYAMCUBR_CMD_STATE, tuyamcubr_recv_status }, +}; + +static void +tuyamcubr_recv(struct tuyamcubr_softc *sc, const struct tuyamcubr_parser *p) +{ + const struct tuyamcubr_recv_command *r; + const uint8_t *data = p->p_data; + size_t len = p->p_len; + size_t i; + + if (len > 0) { + AddLog(LOG_LEVEL_DEBUG, + TUYAMCUBR_FMT("recv version 0x%02x command 0x%02x: %*_H"), + p->p_version, p->p_command, len, data); + } else { + AddLog(LOG_LEVEL_DEBUG, + TUYAMCUBR_FMT("recv version 0x%02x command 0x%02x"), + p->p_version, p->p_command); + } + + for (i = 0; i < nitems(tuyamcubr_recv_commands); i++) { + r = &tuyamcubr_recv_commands[i]; + + if (r->r_command == p->p_command) { + r->r_func(sc, p->p_version, data, len); + return; + } + } + + /* unhandled command? */ +} + +static enum tuyamcubr_parser_state +tuyamcubr_parse(struct tuyamcubr_softc *sc, uint8_t byte) +{ + struct tuyamcubr_parser *p = &sc->sc_parser; + enum tuyamcubr_parser_state nstate = p->p_state; + + switch (p->p_state) { + case TUYAMCUBR_P_START: + if (byte != TUYAMCUBR_H_ONE) + return (TUYAMCUBR_P_START); + + /* reset state */ + p->p_sum = 0; + + nstate = TUYAMCUBR_P_HEADER; + break; + case TUYAMCUBR_P_HEADER: + if (byte != TUYAMCUBR_H_TWO) + return (TUYAMCUBR_P_START); + + nstate = TUYAMCUBR_P_VERSION; + break; + case TUYAMCUBR_P_VERSION: + p->p_version = byte; + nstate = TUYAMCUBR_P_COMMAND; + break; + case TUYAMCUBR_P_COMMAND: + p->p_command = byte; + nstate = TUYAMCUBR_P_LEN1; + break; + + case TUYAMCUBR_P_LEN1: + p->p_len = (uint16_t)byte << 8; + nstate = TUYAMCUBR_P_LEN2; + break; + case TUYAMCUBR_P_LEN2: + p->p_len |= (uint16_t)byte; + p->p_off = 0; + + if (p->p_len > sizeof(p->p_data)) { + AddLog(LOG_LEVEL_DEBUG, + TUYAMCUBR_FMT("skipping command %02x" + ", too much data %zu/%zu"), p->p_command, + p->p_len, sizeof(p->p_data)); + return (TUYAMCUBR_P_SKIP); + } + + nstate = (p->p_len > 0) ? TUYAMCUBR_P_DATA : TUYAMCUBR_P_CKSUM; + break; + + case TUYAMCUBR_P_DATA: + p->p_data[p->p_off++] = byte; + if (p->p_off >= p->p_len) + nstate = TUYAMCUBR_P_CKSUM; + break; + + case TUYAMCUBR_P_CKSUM: + if (tuyamcubr_cksum_fini(p->p_sum) != byte) { + AddLog(LOG_LEVEL_DEBUG, + TUYAMCUBR_FMT("checksum failed, skipping")); + return (TUYAMCUBR_P_START); + } + + tuyamcubr_recv(sc, p); + + /* this message is done, wait for another */ + return (TUYAMCUBR_P_START); + + case TUYAMCUBR_P_SKIP: + if (++p->p_off >= p->p_len) + return (TUYAMCUBR_P_SKIP_CKSUM); + return (nstate); + case TUYAMCUBR_P_SKIP_CKSUM: + return (TUYAMCUBR_P_START); + } + + p->p_sum += byte; + + return (nstate); +} + +static uint8_t +tuyamcubr_write(struct tuyamcubr_softc *sc, const void *data, size_t len) +{ + TasmotaSerial *serial = sc->sc_serial; + const uint8_t *bytes = (const uint8_t *)data; + uint8_t cksum = 0; + size_t i; + + for (i = 0; i < len; i++) { + uint8_t b = bytes[i]; + serial->write(b); + cksum += b; + } + + return (cksum); +} + +static void +tuyamcubr_send(struct tuyamcubr_softc *sc, uint8_t command, + const void *data, size_t len) +{ + TasmotaSerial *serial = sc->sc_serial; + struct tuyamcubr_header h = { + .header = { TUYAMCUBR_H_ONE, TUYAMCUBR_H_TWO }, + .version = 0x00, + .command = command, + .datalen = htons(len), + }; + uint8_t cksum = 0; + + if (len) { + AddLog(LOG_LEVEL_DEBUG, + TUYAMCUBR_FMT("send version 0x%02x command 0x%02x: %*_H"), + h.version, h.command, len, data); + } else { + AddLog(LOG_LEVEL_DEBUG, + TUYAMCUBR_FMT("send version 0x%02x command 0x%02x"), + h.version, h.command); + } + + cksum += tuyamcubr_write(sc, &h, sizeof(h)); + if (len > 0) + cksum += tuyamcubr_write(sc, data, len); + cksum = tuyamcubr_cksum_fini(cksum); + serial->write(cksum); + serial->flush(); +} + +/* if we have polymorphic funcions then we may as well (ab)use them */ +static void +tuyamcubr_send(struct tuyamcubr_softc *sc, uint8_t command) +{ + tuyamcubr_send(sc, command, NULL, 0); +} + +static void +tuyamcubr_heartbeat(struct tuyamcubr_softc *sc, unsigned int deadline) +{ + sc->sc_deadline += deadline; + tuyamcubr_send(sc, TUYAMCUBR_CMD_HEARTBEAT); +} + +static struct tuyamcubr_dp * +tuyamcubr_find_dp(struct tuyamcubr_softc *sc, uint32_t index, uint8_t type) +{ + struct tuyamcubr_dp *dp; + + if (index > 0xff) + return (NULL); + + STAILQ_FOREACH(dp, &sc->sc_dps, dp_entry) { + if (dp->dp_id == index && + dp->dp_type == type) + return (dp); + } + + return (NULL); +} + +static void +tuyamcubr_cmnd_data(struct tuyamcubr_softc *sc, uint8_t type) +{ + const struct tuyamcubr_data_type *dt = &tuyamcubr_data_types[type]; + struct { + struct tuyamcubr_data_header h; + uint8_t value[4]; /* only up to 4 bytes */ + } data; + size_t len = sizeof(data.h) + dt->t_len; + struct tuyamcubr_dp *dp; + + dp = tuyamcubr_find_dp(sc, XdrvMailbox.index, type); + if (dp == NULL) { + ResponseCmndChar_P(PSTR("Unknown DpId")); + return; + } + + if (XdrvMailbox.data_len == 0) { + ResponseCmndNumber(dp->dp_value); + return; + } + + if (XdrvMailbox.payload < 0x00 || XdrvMailbox.payload > dt->t_max) { + ResponseCmndChar_P(PSTR("Invalid")); + return; + } + + dp->dp_value = XdrvMailbox.payload; + + data.h.dpid = dp->dp_id; + data.h.type = dp->dp_type; + data.h.len = htons(dt->t_len); + dt->t_wr(data.value, dp->dp_value); + + tuyamcubr_send(sc, TUYAMCUBR_CMD_SET_DP, &data, len); + tuyamcubr_rule_dp(sc, dp); + + ResponseCmndNumber(dp->dp_value); + + /* SetOption59 */ + if (Settings->flag3.hass_tele_on_power) + tuyamcubr_publish_dp(sc, dp); +} + +static void +tuyamcubr_cmnd_data_bool(void) +{ + tuyamcubr_cmnd_data(tuyamcubr_sc, TUYAMCUBR_DATA_TYPE_BOOL); +} + +static void +tuyamcubr_cmnd_data_value(void) +{ + tuyamcubr_cmnd_data(tuyamcubr_sc, TUYAMCUBR_DATA_TYPE_VALUE); +} + +static void +tuyamcubr_cmnd_data_enum(void) +{ + tuyamcubr_cmnd_data(tuyamcubr_sc, TUYAMCUBR_DATA_TYPE_ENUM); +} + +static void +tuyamcubr_rule_dp(struct tuyamcubr_softc *sc, const struct tuyamcubr_dp *dp) +{ + const struct tuyamcubr_data_type *dt = + &tuyamcubr_data_types[dp->dp_type]; + + /* XXX this only handles numeric types */ + + Response_P(PSTR("{\"%s\":{\"%s%u\":%u}}"), + D_CMND_TUYAMCUBR_PREFIX, + dt->t_name, dp->dp_id, + dp->dp_value); + XdrvRulesProcess(0); +} + +static void +tuyamcubr_publish_dp(struct tuyamcubr_softc *sc, const struct tuyamcubr_dp *dp) +{ + const struct tuyamcubr_data_type *dt = + &tuyamcubr_data_types[dp->dp_type]; + char topic[64]; /* how long is a (bit of) string? */ + + /* XXX this only handles numeric types */ + + snprintf(topic, sizeof(topic), PSTR("%s%s%u"), + D_CMND_TUYAMCUBR_PREFIX, dt->t_name, dp->dp_id); + Response_P(PSTR("%u"), dp->dp_value); + MqttPublishPrefixTopic_P(TELE, topic); +} + +static void +tuyamcubr_publish(struct tuyamcubr_softc *sc) +{ + struct tuyamcubr_dp *dp; + + STAILQ_FOREACH(dp, &sc->sc_dps, dp_entry) + tuyamcubr_publish_dp(sc, dp); +} + +static void +tuyamcubr_send_heartbeat(struct tuyamcubr_softc *sc, unsigned int deadline) +{ + sc->sc_deadline += deadline; + tuyamcubr_send(sc, TUYAMCUBR_CMD_HEARTBEAT); +} + +static void +tuyamcubr_recv_heartbeat(struct tuyamcubr_softc *sc, uint8_t v, + const uint8_t *data, size_t datalen) +{ + /* check the data? */ + + switch (sc->sc_state) { + case TUYAMCUBR_S_START: + sc->sc_state = TUYAMCUBR_S_PROD_INFO; + tuyamcubr_send(sc, TUYAMCUBR_CMD_PRODUCT); + break; + case TUYAMCUBR_S_RUNNING: + sc->sc_waiting = 0; + break; + default: + AddLog(LOG_LEVEL_ERROR, + TUYAMCUBR_FMT("unexpected heartbeat in state %u"), + sc->sc_state); + break; + } +} + +static void +tuyamcubr_recv_product_info(struct tuyamcubr_softc *sc, uint8_t v, + const uint8_t *data, size_t datalen) +{ + AddLog(LOG_LEVEL_INFO, TUYAMCUBR_FMT("MCU Product ID: %.*s"), + datalen, data); + + switch (sc->sc_state) { + case TUYAMCUBR_S_PROD_INFO: + sc->sc_state = TUYAMCUBR_S_MODE; + tuyamcubr_send(sc, TUYAMCUBR_CMD_MODE); + break; + default: + AddLog(LOG_LEVEL_ERROR, + TUYAMCUBR_FMT("unexpected product info in state %u"), + sc->sc_state); + break; + } +} + +static void +tuyamcubr_recv_mode(struct tuyamcubr_softc *sc, uint8_t v, + const uint8_t *data, size_t datalen) +{ + switch (sc->sc_state) { + case TUYAMCUBR_S_MODE: + switch (datalen) { + case 0: + AddLog(LOG_LEVEL_INFO, + TUYAMCUBR_FMT("MCU Mode: Coordinated")); + break; + case 2: + AddLog(LOG_LEVEL_INFO, TUYAMCUBR_FMT("MCU Mode" + ": Status GPIO%u, Reset GPIO%u"), + data[0], data[1]); + + sc->sc_state = TUYAMCUBR_S_RUNNING; + tuyamcubr_send(sc, TUYAMCUBR_CMD_QUERY_STATE); + return; + default: + AddLog(LOG_LEVEL_ERROR, TUYAMCUBR_FMT("MCU Mode" + ": unexpected data length %zu"), datalen); + break; + } + + sc->sc_state = TUYAMCUBR_S_NET_STATUS; + tuyamcubr_send(sc, TUYAMCUBR_CMD_WIFI_STATE, + &sc->sc_network_status, sizeof(sc->sc_network_status)); + break; + default: + AddLog(LOG_LEVEL_ERROR, + TUYAMCUBR_FMT("unexpected product info in state %u"), + sc->sc_state); + break; + } +} + +static void +tuyamcubr_recv_net_status(struct tuyamcubr_softc *sc, uint8_t v, + const uint8_t *data, size_t datalen) +{ + switch (sc->sc_state) { + case TUYAMCUBR_S_NET_STATUS: + sc->sc_state = TUYAMCUBR_S_RUNNING; + tuyamcubr_send(sc, TUYAMCUBR_CMD_QUERY_STATE); + break; + default: + AddLog(LOG_LEVEL_ERROR, + TUYAMCUBR_FMT("unexpected product info in state %u"), + sc->sc_state); + break; + } +} + +static void +tuyamcubr_recv_status(struct tuyamcubr_softc *sc, uint8_t v, + const uint8_t *data, size_t datalen) +{ + const struct tuyamcubr_data_type *dt; + struct tuyamcubr_dp *dp; + struct tuyamcubr_data_header h; + size_t len; + const uint8_t *b; + uint32_t value; + + /* take dp status updates at any time */ + + do { + if (datalen < sizeof(h)) { + AddLog(LOG_LEVEL_ERROR, + TUYAMCUBR_FMT("status header short %zu<%zu"), + datalen, sizeof(h)); + return; + } + + memcpy(&h, data, sizeof(h)); + data += sizeof(h); + datalen -= sizeof(h); + + len = ntohs(h.len); + if (datalen < len) { + AddLog(LOG_LEVEL_ERROR, + TUYAMCUBR_FMT("status data short %zu<%zu"), + datalen, len); + return; + } + + b = data; + data += len; + datalen -= len; + + dt = tuyamcubr_find_data_type(h.type); + if (dt == NULL || + dt->t_len == -1) { /* XXX revisit this */ + AddLog(LOG_LEVEL_INFO, + TUYAMCUBR_FMT("DpId %u unsupported type 0x%02x"), + h.dpid, h.type); + continue; + } + + if (len != dt->t_len) { + AddLog(LOG_LEVEL_ERROR, + TUYAMCUBR_FMT("%s%s%u: unexpected len %zu"), + D_CMND_TUYAMCUBR_PREFIX, dt->t_name, len); + continue; + } + + value = dt->t_rd(b); + if (value > dt->t_max) { + AddLog(LOG_LEVEL_ERROR, + TUYAMCUBR_FMT("%s%s%u: unexpected value %u>%u"), + D_CMND_TUYAMCUBR_PREFIX, dt->t_name, value, + dt->t_max); + continue; + } + + dp = tuyamcubr_find_dp(sc, h.dpid, h.type); + if (dp == NULL) { + dp = (struct tuyamcubr_dp *)malloc(sizeof(*dp)); + if (dp == NULL) { + AddLog(LOG_LEVEL_ERROR, + TUYAMCUBR_FMT("%s%s%u no memory"), + D_CMND_TUYAMCUBR_PREFIX, + tuyamcubr_data_types[h.type], h.dpid); + continue; + } + + dp->dp_id = h.dpid; + dp->dp_type = h.type; + STAILQ_INSERT_TAIL(&sc->sc_dps, dp, dp_entry); + } else if (dp->dp_value == value) { + /* nop */ + continue; + } + + dp->dp_value = value; + tuyamcubr_rule_dp(sc, dp); + tuyamcubr_publish_dp(sc, dp); + } while (datalen > 0); +} + +static void +tuyamcubr_tick(struct tuyamcubr_softc *sc, unsigned int ms) +{ + int diff; + + sc->sc_clock += ms; + + diff = sc->sc_clock - sc->sc_deadline; + if (diff < 0) { + /* deadline hasn't been reached, nothing to do */ + return; + } + + switch (sc->sc_state) { + case TUYAMCUBR_S_START: + tuyamcubr_send_heartbeat(sc, 3000); + break; + case TUYAMCUBR_S_RUNNING: + tuyamcubr_send_heartbeat(sc, 15000); + if (sc->sc_waiting) { + AddLog(LOG_LEVEL_ERROR, + TUYAMCUBR_FMT("no heartbeat response")); + /* XXX restart? */ + } + sc->sc_waiting = 1; + break; + } +} + +static void +tuyamcubr_every_1sec(struct tuyamcubr_softc *sc) +{ + /* start with the assumption that wifi is configured */ + uint8_t network_status = TUYAMCUBR_NETWORK_STATUS_3; + + if (MqttIsConnected()) { + /* the device is connected to the "cloud" */ + network_status = TUYAMCUBR_NETWORK_STATUS_5; + } else { + switch (WifiState()) { + case WIFI_MANAGER: + /* Pairing in AP mode */ + network_status = TUYAMCUBR_NETWORK_STATUS_2; + break; + case WIFI_RESTART: + /* WiFi + router */ + network_status = TUYAMCUBR_NETWORK_STATUS_4; + break; + } + } + + if (sc->sc_network_status != network_status) { + sc->sc_network_status = network_status; + + if (sc->sc_state == TUYAMCUBR_S_RUNNING) { + tuyamcubr_send(sc, TUYAMCUBR_CMD_WIFI_STATE, + &network_status, sizeof(network_status)); + } + } +} + +static void +tuyamcubr_pre_init(void) +{ + struct tuyamcubr_softc *sc; + int baudrate; + + /* + * SetOption97 - Set Baud rate for TuyaMCU serial communication + * (0 = 9600 or 1 = 115200) + */ + baudrate = (Settings->flag4.tuyamcu_baudrate) ? 115200 : 9600; + + if (!PinUsed(GPIO_TUYAMCUBR_TX) || !PinUsed(GPIO_TUYAMCUBR_RX)) + return; + + sc = (struct tuyamcubr_softc *)calloc(1, sizeof(*sc)); + if (sc == NULL) { + AddLog(LOG_LEVEL_ERROR, + TUYAMCUBR_FMT("unable to allocate state")); + return; + } + + sc->sc_parser.p_state = TUYAMCUBR_P_START; + + sc->sc_state = TUYAMCUBR_S_START; + sc->sc_clock = 0; + sc->sc_network_status = (WifiState() == WIFI_MANAGER) ? + TUYAMCUBR_NETWORK_STATUS_2 : TUYAMCUBR_NETWORK_STATUS_3; + STAILQ_INIT(&sc->sc_dps); + + sc->sc_serial = new TasmotaSerial(Pin(GPIO_TUYAMCUBR_RX), + Pin(GPIO_TUYAMCUBR_TX), 2); + if (!sc->sc_serial->begin(baudrate)) { + AddLog(LOG_LEVEL_ERROR, + TUYAMCUBR_FMT("unable to begin serial (baudrate %d)"), + baudrate); + goto del; + } + + if (sc->sc_serial->hardwareSerial()) + ClaimSerial(); + + /* commit */ + tuyamcubr_sc = sc; + + /* kick the state machine off */ + tuyamcubr_tick(sc, 0); + return; +del: + delete sc->sc_serial; +free: + free(sc); +} + +static void +tuyamcubr_loop(struct tuyamcubr_softc *sc) +{ + TasmotaSerial *serial = sc->sc_serial; + + while (serial->available()) { + yield(); + sc->sc_parser.p_state = tuyamcubr_parse(sc, serial->read()); + } +} + +/* + * Interface + */ + +static const char tuyamcubr_cmnd_names[] PROGMEM = + D_CMND_TUYAMCUBR_PREFIX + "|" D_CMND_TUYAMCUBR_DATA_BOOL + "|" D_CMND_TUYAMCUBR_DATA_VALUE + "|" D_CMND_TUYAMCUBR_DATA_ENUM + ; + +static void (*const tuyamcubr_cmnds[])(void) PROGMEM = { + &tuyamcubr_cmnd_data_bool, + &tuyamcubr_cmnd_data_value, + &tuyamcubr_cmnd_data_enum, +}; + +bool +Xdrv65(uint32_t function) +{ + bool result = false; + struct tuyamcubr_softc *sc; + + switch (function) { + case FUNC_PRE_INIT: + tuyamcubr_pre_init(); + return (false); + } + + sc = tuyamcubr_sc; + if (sc == NULL) + return (false); + + switch (function) { + case FUNC_LOOP: + tuyamcubr_loop(sc); + break; + +#if 0 + case FUNC_SET_DEVICE_POWER: + result = tuyamcubr_set_power(sc); + break; +#endif + + case FUNC_EVERY_100_MSECOND: + tuyamcubr_tick(sc, 100); + break; + + case FUNC_EVERY_50_MSECOND: + case FUNC_EVERY_200_MSECOND: + case FUNC_EVERY_250_MSECOND: + break; + case FUNC_EVERY_SECOND: + tuyamcubr_every_1sec(sc); + break; + +#if 0 + case FUNC_JSON_APPEND: + tuyamcubr_sensor(sc); + break; +#endif + case FUNC_AFTER_TELEPERIOD: + tuyamcubr_publish(sc); + break; + + case FUNC_COMMAND: + result = DecodeCommand(tuyamcubr_cmnd_names, tuyamcubr_cmnds); + break; + } + + return (result); +} + +#endif // USE_TUYAMCUBR diff --git a/tools/decode-status.py b/tools/decode-status.py index efc5d0d48..843a2d0e8 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -290,7 +290,7 @@ a_features = [[ "USE_SGP40","USE_LUXV30B","USE_CANSNIFFER","USE_QMC5883L", "USE_MODBUS_ENERGY","USE_SHELLY_PRO","USE_DALI","USE_BP1658CJ", "USE_DINGTIAN_RELAY","USE_HMC5883L","USE_LD2410","USE_ME007", - "USE_DISPLAY_TM1650","USE_PCA9632","","", + "USE_DISPLAY_TM1650","USE_PCA9632","USE_TUYAMCUBR","", "","","","", "","","","", "","","","", diff --git a/tools/lv_gpio/lv_gpio_enum.h b/tools/lv_gpio/lv_gpio_enum.h index d57740364..70b297099 100644 --- a/tools/lv_gpio/lv_gpio_enum.h +++ b/tools/lv_gpio/lv_gpio_enum.h @@ -209,6 +209,8 @@ ZIGBEE_RST = GPIO_ZIGBEE_RST DYP_RX = GPIO_DYP_RX MIEL_HVAC_TX = GPIO_MIEL_HVAC_TX MIEL_HVAC_RX = GPIO_MIEL_HVAC_RX +TUYAMCUBR_TX = GPIO_TUYAMCUBR_TX +TUYAMCUBR_RX = GPIO_TUYAMCUBR_RX WE517_TX = GPIO_WE517_TX WE517_RX = GPIO_WE517_RX AS608_TX = GPIO_AS608_TX