2018-10-18 12:01:31 +01:00
/*
2019-10-27 10:13:24 +00:00
xdrv_16_tuyamcu . ino - Tuya MCU support for Tasmota
2018-10-18 12:01:31 +01:00
2021-01-01 12:44:04 +00:00
Copyright ( C ) 2021 Federico Leoni , digiblur , Joel Stein and Theo Arends
2018-10-18 12:01:31 +01:00
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2019-06-16 15:43:23 +01:00
# ifdef USE_LIGHT
2019-09-03 12:23:05 +01:00
# ifdef USE_TUYA_MCU
2018-10-18 12:01:31 +01:00
2018-11-07 09:30:03 +00:00
# define XDRV_16 16
2020-07-14 16:37:14 +01:00
# define XNRG_32 32 // Needs to be the last XNRG_xx
2018-11-07 09:30:03 +00:00
2018-10-18 12:01:31 +01:00
# ifndef TUYA_DIMMER_ID
2018-11-03 21:34:29 +00:00
# define TUYA_DIMMER_ID 0
2018-10-18 12:01:31 +01:00
# endif
2018-11-03 21:34:29 +00:00
2022-04-01 16:33:57 +01:00
# define TUYA_CMD_HEARTBEAT 0x00
# define TUYA_CMD_QUERY_PRODUCT 0x01
# define TUYA_CMD_MCU_CONF 0x02
# define TUYA_CMD_WIFI_STATE 0x03
# define TUYA_CMD_WIFI_RESET 0x04
# define TUYA_CMD_WIFI_SELECT 0x05
# define TUYA_CMD_SET_DP 0x06
# define TUYA_CMD_STATE 0x07
# define TUYA_CMD_QUERY_STATE 0x08
# define TUYA_CMD_INITIATING_UPGRADE 0x0A
# define TUYA_CMD_UPGRADE_PACKAGE 0x0B
# define TUYA_CMD_SET_TIME 0x1C
2018-11-03 21:34:29 +00:00
2019-10-14 14:20:47 +01:00
# define TUYA_LOW_POWER_CMD_WIFI_STATE 0x02
# define TUYA_LOW_POWER_CMD_WIFI_RESET 0x03
# define TUYA_LOW_POWER_CMD_WIFI_CONFIG 0x04
# define TUYA_LOW_POWER_CMD_STATE 0x05
2022-03-29 23:08:27 +01:00
# define TUYA_TYPE_RAW 0x00
2018-11-03 21:34:29 +00:00
# define TUYA_TYPE_BOOL 0x01
# define TUYA_TYPE_VALUE 0x02
2019-10-22 17:41:31 +01:00
# define TUYA_TYPE_STRING 0x03
2019-10-14 14:20:47 +01:00
# define TUYA_TYPE_ENUM 0x04
2018-11-03 21:34:29 +00:00
# define TUYA_BUFFER_SIZE 256
2018-10-18 12:01:31 +01:00
2018-10-27 02:01:09 +01:00
# include <TasmotaSerial.h>
2018-10-27 03:11:30 +01:00
TasmotaSerial * TuyaSerial = nullptr ;
2018-10-27 02:01:09 +01:00
2019-08-17 14:00:57 +01:00
struct TUYA {
2020-09-16 19:45:56 +01:00
uint16_t Levels [ 5 ] = { 0 , 0 , 0 , 0 , 0 } ; // Array to store the values of TuyaMCU channels
2020-11-07 21:08:07 +00:00
uint16_t Snapshot [ 5 ] = { 0 , 0 , 0 , 0 , 0 } ; // Array to store a snapshot of Tasmota actual channel values
uint16_t EnumState [ 4 ] = { 0 , 0 , 0 , 0 } ; // Array to store up to four Enum type 4 values
char RGBColor [ 7 ] = " 000000 " ; // Stores RGB Color string in Hex format
2020-09-16 19:45:56 +01:00
uint16_t CTMin = 153 ; // Minimum CT level allowed - When SetOption82 is enabled will default to 200
uint16_t CTMax = 500 ; // Maximum CT level allowed - When SetOption82 is enabled will default to 380
bool ModeSet = false ; // Controls 0 - Single Tone light, 1 - RGB Light
2021-02-18 15:34:33 +00:00
int16_t Sensors [ 14 ] ; // Stores the values of Sensors connected to the Tuya Device
2020-11-28 14:49:04 +00:00
bool SensorsValid [ 14 ] ; // Bool used for nullify the sensor value until a real value is received from the MCU
2020-09-16 19:45:56 +01:00
bool SuspendTopic = false ; // Used to reduce the load at init time or when polling the configuraton on demand
uint32_t ignore_topic_timeout = 0 ; // Suppress the /STAT topic (if enabled) to avoid data overflow until the configuration is over
bool ignore_dim = false ; // Flag to skip serial send to prevent looping when processing inbound states from the faceplate interaction
uint8_t cmd_status = 0 ; // Current status of serial-read
uint8_t cmd_checksum = 0 ; // Checksum of tuya command
uint8_t data_len = 0 ; // Data lenght of command
2019-10-14 14:20:47 +01:00
uint8_t wifi_state = - 2 ; // Keep MCU wifi-status in sync with WifiState()
2020-09-16 19:45:56 +01:00
uint8_t heartbeat_timer = 0 ; // 10 second heartbeat timer for tuya module
2019-08-27 14:40:43 +01:00
# ifdef USE_ENERGY_SENSOR
2020-09-16 19:45:56 +01:00
uint32_t lastPowerCheckTime = 0 ; // Time when last power was checked
2019-08-27 14:40:43 +01:00
# endif // USE_ENERGY_SENSOR
2020-09-16 19:45:56 +01:00
char * buffer = nullptr ; // Serial receive buffer
int byte_counter = 0 ; // Index in serial receive buffer
bool low_power_mode = false ; // Normal or Low power mode protocol
bool send_success_next_second = false ; // Second command success in low power mode
uint32_t ignore_dimmer_cmd_timeout = 0 ; // Time until which received dimmer commands should be ignored
bool ignore_tuyareceived = false ; // When a modeset changes ignore stat
2021-12-19 15:41:10 +00:00
bool active ;
2019-08-17 14:00:57 +01:00
} Tuya ;
2018-10-27 10:37:42 +01:00
2020-11-08 11:51:08 +00:00
# define D_JSON_TUYA_MCU_RECEIVED "TuyaReceived"
2020-11-07 21:08:07 +00:00
2020-11-08 11:51:08 +00:00
# define D_PRFX_TUYA "Tuya"
# define D_CMND_TUYA_MCU "MCU"
# define D_CMND_TUYA_MCU_SEND_STATE "Send"
# define D_CMND_TUYARGB "RGB"
# define D_CMND_TUYA_ENUM "Enum"
# define D_CMND_TUYA_ENUM_LIST "EnumList"
2021-04-01 20:20:11 +01:00
// #define D_CMND_TUYA_SET_TEMP "SetTemp"
// #define D_CMND_TUYA_SET_HUM "SetHum"
// #define D_CMND_TUYA_SET_TIMER "SetTimer"
2020-11-28 14:49:04 +00:00
2021-03-29 19:32:59 +01:00
const char kTuyaSensors [ ] PROGMEM = // List of available sensors (can be expanded in the future)
2022-03-26 13:45:13 +00:00
// 71 72 73 74 75
2020-11-28 14:49:04 +00:00
" " D_JSON_TEMPERATURE " |TempSet| " D_JSON_HUMIDITY " |HumSet| " D_JSON_ILLUMINANCE
2022-08-14 23:58:45 +01:00
// 76 77 78 79 80 81 82 83 84
" | " D_JSON_TVOC " | " D_JSON_ECO2 " | " D_JSON_CO2 " | " D_JSON_GAS " | " D_ENVIRONMENTAL_CONCENTRATION " |Timer1|Timer2|Timer3|TImer4 " ;
2020-11-08 11:51:08 +00:00
const char kTuyaCommand [ ] PROGMEM = D_PRFX_TUYA " | " // Prefix
2021-04-19 11:25:53 +01:00
D_CMND_TUYA_MCU " | " D_CMND_TUYA_MCU_SEND_STATE " | " D_CMND_TUYARGB " | " D_CMND_TUYA_ENUM " | " D_CMND_TUYA_ENUM_LIST " |TempSetRes " ;
2019-09-03 11:23:44 +01:00
void ( * const TuyaCommand [ ] ) ( void ) PROGMEM = {
2021-04-19 11:25:53 +01:00
& CmndTuyaMcu , & CmndTuyaSend , & CmndTuyaRgb , & CmndTuyaEnum , & CmndTuyaEnumList , & CmndTuyaTempSetRes
2019-09-03 11:23:44 +01:00
} ;
2022-04-01 17:21:36 +01:00
const uint8_t TuyaExcludeCMDsFromMQTT [ ] PROGMEM = { // don't publish this received commands via MQTT if SetOption66 and SetOption137 is active (can be expanded in the future)
TUYA_CMD_HEARTBEAT , TUYA_CMD_WIFI_STATE , TUYA_CMD_SET_TIME , TUYA_CMD_UPGRADE_PACKAGE
} ;
2022-04-01 16:33:57 +01:00
2020-09-16 19:45:56 +01:00
/*********************************************************************************************\
* Web Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-12-19 15:41:10 +00:00
bool IsModuleTuya ( void ) {
bool is_tuya = Tuya . active ;
//#ifdef ESP8266
// This is not a Tuya driven device. It uses a Tuya provided ESP8266. Why it was here is a mystery to me.
// if (SK03_TUYA == TasmotaGlobal.module_type) {
// is_tuya = true;
// }
//#endif
return is_tuya ;
2020-09-16 19:45:56 +01:00
}
2019-10-22 17:41:31 +01:00
2020-09-16 19:45:56 +01:00
bool AsModuleTuyaMS ( void ) // ModeSet Layout
{
2020-10-30 11:29:48 +00:00
return ( ( TasmotaGlobal . light_type > LT_RGB ) & & TuyaGetDpId ( TUYA_MCU_FUNC_MODESET ) ! = 0 ) ;
2020-09-16 19:45:56 +01:00
}
bool TuyaModeSet ( void ) // ModeSet Status
{
return Tuya . ModeSet ;
}
2021-12-19 15:41:10 +00:00
2020-11-28 14:49:04 +00:00
/*********************************************************************************************\
* Web Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-09-16 19:45:56 +01:00
/*
2019-10-22 17:41:31 +01:00
TuyaSend < x > dpId , data
2020-02-09 07:44:05 +00:00
TuyaSend0 - > Sends TUYA_CMD_QUERY_STATE
2019-10-22 17:41:31 +01:00
TuyaSend1 11 , 1 - > Sends boolean ( Type 1 ) data 0 / 1 to dpId 11 ( Max data length 1 byte )
TuyaSend2 11 , 100 - > Sends integer ( Type 2 ) data 100 to dpId 11 ( Max data length 4 bytes )
TuyaSend2 11 , 0xAABBCCDD - > Sends 4 bytes ( Type 2 ) data to dpId 11 ( Max data length 4 bytes )
TuyaSend3 11 , ThisIsTheData - > Sends the supplied string ( Type 3 ) to dpId 11 ( Max data length not - known )
2020-11-28 14:49:04 +00:00
TuyaSend4 11 , 1 - > Sends enum ( Type 4 ) data 1 to dpId 11 ( Max data length 1 bytes )
2022-08-20 12:46:31 +01:00
TuyaSend5 11 , ABCD - > Sends an HEX string ( Type 3 ) data to dpId
2022-03-29 23:08:27 +01:00
TuyaSend6 11 , ABCD - > Sends raw ( Type 0 ) data to dpId
2019-10-22 17:41:31 +01:00
*/
void CmndTuyaSend ( void ) {
2022-08-20 12:46:31 +01:00
if ( XdrvMailbox . index > 6 & & XdrvMailbox . index < 8 ) {
2020-02-09 07:44:05 +00:00
return ;
}
if ( XdrvMailbox . index = = 0 ) {
2020-09-16 19:45:56 +01:00
TuyaRequestState ( 0 ) ;
2022-08-20 12:46:31 +01:00
ResponseCmndDone ( ) ;
2020-09-16 19:45:56 +01:00
} else if ( XdrvMailbox . index = = 8 ) {
TuyaRequestState ( 8 ) ;
2022-08-20 12:46:31 +01:00
ResponseCmndDone ( ) ;
2020-09-16 19:45:56 +01:00
} else if ( XdrvMailbox . index = = 9 ) { // TuyaSend Topic Toggle
2021-06-11 17:14:12 +01:00
Settings - > tuyamcu_topic = ! Settings - > tuyamcu_topic ;
AddLog ( LOG_LEVEL_INFO , PSTR ( " TYA: TuyaMCU Stat Topic %s " ) , ( Settings - > tuyamcu_topic ? PSTR ( " enabled " ) : PSTR ( " disabled " ) ) ) ;
2022-08-20 12:46:31 +01:00
ResponseCmndDone ( ) ;
2020-02-09 07:44:05 +00:00
} else {
2019-10-22 17:41:31 +01:00
if ( XdrvMailbox . data_len > 0 ) {
char * p ;
2022-08-20 12:46:31 +01:00
char * data = nullptr ;
2019-10-22 17:41:31 +01:00
uint8_t i = 0 ;
uint8_t dpId = 0 ;
for ( char * str = strtok_r ( XdrvMailbox . data , " , " , & p ) ; str & & i < 2 ; str = strtok_r ( nullptr , " , " , & p ) ) {
if ( i = = 0 ) {
dpId = strtoul ( str , nullptr , 0 ) ;
} else {
data = str ;
}
i + + ;
}
2022-08-20 12:46:31 +01:00
if ( data ) {
if ( 1 = = XdrvMailbox . index ) {
TuyaSendBool ( dpId , strtoul ( data , nullptr , 0 ) ) ;
} else if ( 2 = = XdrvMailbox . index ) {
TuyaSendValue ( dpId , strtoull ( data , nullptr , 0 ) ) ;
} else if ( 3 = = XdrvMailbox . index ) {
TuyaSendString ( dpId , data ) ;
} else if ( 5 = = XdrvMailbox . index ) {
TuyaSendHexString ( dpId , data ) ;
} else if ( 4 = = XdrvMailbox . index ) {
TuyaSendEnum ( dpId , strtoul ( data , nullptr , 0 ) ) ;
} else if ( 6 = = XdrvMailbox . index ) {
TuyaSendRaw ( dpId , data ) ;
}
ResponseCmndDone ( ) ;
2019-10-22 17:41:31 +01:00
}
}
}
2022-08-20 12:46:31 +01:00
// {"TuyaSend":"error"}
2019-10-22 17:41:31 +01:00
}
2019-09-03 11:23:44 +01:00
2020-09-16 19:45:56 +01:00
// TuyaMcu fnid,dpid
2019-09-03 11:23:44 +01:00
void CmndTuyaMcu ( void ) {
if ( XdrvMailbox . data_len > 0 ) {
char * p ;
uint8_t i = 0 ;
uint8_t parm [ 3 ] = { 0 } ;
for ( char * str = strtok_r ( XdrvMailbox . data , " , " , & p ) ; str & & i < 2 ; str = strtok_r ( nullptr , " , " , & p ) ) {
parm [ i ] = strtoul ( str , nullptr , 0 ) ;
i + + ;
}
if ( TuyaFuncIdValid ( parm [ 0 ] ) ) {
2020-09-16 19:45:56 +01:00
bool DualDim ;
if ( TUYA_MCU_FUNC_DIMMER2 = = parm [ 0 ] & & parm [ 1 ] ! = 0 ) {
if ( TuyaGetDpId ( TUYA_MCU_FUNC_DIMMER ) ! = 0 ) { DualDim = true ; }
} else if ( TUYA_MCU_FUNC_DIMMER = = parm [ 0 ] & & parm [ 1 ] ! = 0 ) {
if ( TuyaGetDpId ( TUYA_MCU_FUNC_DIMMER2 ) ! = 0 ) { DualDim = true ; }
} else if ( ( TUYA_MCU_FUNC_DIMMER = = parm [ 0 ] & & parm [ 1 ] = = 0 ) | | ( TUYA_MCU_FUNC_DIMMER2 = = parm [ 0 ] & & parm [ 1 ] = = 0 ) ) { DualDim = false ; } ;
2020-11-07 21:08:07 +00:00
if ( DualDim ) { // If the second dimmer is enabled CT, RGB or WHITE function must be removed
if ( TuyaGetDpId ( TUYA_MCU_FUNC_CT ) ! = 0 ) { TuyaAddMcuFunc ( TUYA_MCU_FUNC_CT , 0 ) ; }
2020-09-16 19:45:56 +01:00
if ( TuyaGetDpId ( TUYA_MCU_FUNC_RGB ) ! = 0 ) { TuyaAddMcuFunc ( TUYA_MCU_FUNC_RGB , 0 ) ; }
if ( TuyaGetDpId ( TUYA_MCU_FUNC_WHITE ) ! = 0 ) { TuyaAddMcuFunc ( TUYA_MCU_FUNC_WHITE , 0 ) ; }
2021-06-11 17:14:12 +01:00
Settings - > flag3 . pwm_multi_channels = 1 ;
} else { Settings - > flag3 . pwm_multi_channels = 0 ; }
2019-09-03 11:23:44 +01:00
TuyaAddMcuFunc ( parm [ 0 ] , parm [ 1 ] ) ;
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag = 2 ;
2019-09-03 11:23:44 +01:00
} else {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_ERROR , PSTR ( " TYA: TuyaMcu Invalid function id=%d " ) , parm [ 0 ] ) ;
2019-09-03 11:23:44 +01:00
}
}
2020-11-08 11:51:08 +00:00
Response_P ( PSTR ( " { \" %s \" :[ " ) , XdrvMailbox . command ) ; // Builds TuyaMCU
2019-09-03 11:23:44 +01:00
bool added = false ;
for ( uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS ; i + + ) {
2021-06-11 17:14:12 +01:00
if ( Settings - > tuya_fnid_map [ i ] . fnid ! = 0 ) {
2019-09-03 11:23:44 +01:00
if ( added ) {
ResponseAppend_P ( PSTR ( " , " ) ) ;
}
2021-06-11 17:14:12 +01:00
ResponseAppend_P ( PSTR ( " { \" fnId \" :%d, \" dpId \" :%d} " ) , Settings - > tuya_fnid_map [ i ] . fnid , Settings - > tuya_fnid_map [ i ] . dpid ) ;
2019-09-03 11:23:44 +01:00
added = true ;
}
}
2019-10-10 15:53:01 +01:00
ResponseAppend_P ( PSTR ( " ]} " ) ) ;
2019-09-03 11:23:44 +01:00
}
2020-11-07 21:08:07 +00:00
void CmndTuyaRgb ( void ) { // Command to control the RGB format
uint16_t payload = XdrvMailbox . payload ;
if ( XdrvMailbox . data_len > 0 ) {
if ( payload < 0 | | payload > 3 | | TuyaGetDpId ( TUYA_MCU_FUNC_RGB ) = = 0 ) {
return ;
} else {
2021-06-11 17:14:12 +01:00
if ( payload ! = Settings - > tuya_fnid_map [ 230 ] . dpid ) { // fnid 230 is reserved for RGB
Settings - > tuya_fnid_map [ 230 ] . fnid = 230 ;
Settings - > tuya_fnid_map [ 230 ] . dpid = payload ;
2020-11-07 21:08:07 +00:00
}
}
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > tuya_fnid_map [ 230 ] . dpid ) ;
2020-11-07 21:08:07 +00:00
}
2021-04-19 11:25:53 +01:00
void CmndTuyaTempSetRes ( void )
{
if ( ( XdrvMailbox . payload > = 0 ) & & ( XdrvMailbox . payload < = 3 ) ) {
2021-06-11 17:14:12 +01:00
Settings - > mbflag2 . temperature_set_res = XdrvMailbox . payload ;
2021-04-19 11:25:53 +01:00
}
2021-06-11 17:14:12 +01:00
ResponseCmndNumber ( Settings - > mbflag2 . temperature_set_res ) ;
2021-04-19 11:25:53 +01:00
}
2020-11-08 11:51:08 +00:00
void CmndTuyaEnum ( void ) { // Command to control up to four type 4 Enum
2020-11-07 21:08:07 +00:00
uint16_t EnumIdx = XdrvMailbox . index ;
int32_t payload = XdrvMailbox . payload ;
if ( EnumIdx > 4 | | TuyaGetDpId ( TUYA_MCU_FUNC_ENUM1 + ( EnumIdx - 1 ) ) = = 0 ) {
return ;
}
if ( XdrvMailbox . data_len > 0 ) {
2021-06-11 17:14:12 +01:00
if ( payload < 0 | | payload > Settings - > tuya_fnid_map [ EnumIdx + 230 ] . dpid ) {
2020-11-07 21:08:07 +00:00
return ;
} else {
if ( payload ! = Tuya . EnumState [ EnumIdx - 1 ] ) {
Tuya . EnumState [ EnumIdx - 1 ] = payload ;
TuyaSendEnum ( TuyaGetDpId ( TUYA_MCU_FUNC_ENUM1 + ( EnumIdx - 1 ) ) , payload ) ;
}
2020-11-08 11:51:08 +00:00
ResponseCmndIdxNumber ( Tuya . EnumState [ EnumIdx - 1 ] ) ;
2020-11-07 21:08:07 +00:00
}
} else {
2020-11-08 11:51:08 +00:00
Response_P ( PSTR ( " { \" %s \" :{ " ) , XdrvMailbox . command ) ; // Builds TuyaEnum
2020-11-07 21:08:07 +00:00
bool added = false ;
for ( uint8_t i = 0 ; i < = 3 ; i + + ) {
if ( TuyaGetDpId ( TUYA_MCU_FUNC_ENUM1 + i ) ! = 0 ) {
if ( added ) {
ResponseAppend_P ( PSTR ( " , " ) ) ;
}
2021-03-29 19:32:59 +01:00
ResponseAppend_P ( PSTR ( " \" Enum%d \" :%d " ) , i + 1 , Tuya . EnumState [ i ] ) ; // Returns the actual values of Enum as list
2020-11-07 21:08:07 +00:00
added = true ;
}
}
2020-11-07 21:40:43 +00:00
ResponseAppend_P ( PSTR ( " }} " ) ) ;
2020-11-07 21:08:07 +00:00
}
}
2020-11-28 14:49:04 +00:00
void CmndTuyaEnumList ( void ) { // Command to declare the number of items in list for up to four type 4 enum. Min = 0, Max = 31, Default = 0
2020-11-07 21:08:07 +00:00
if ( XdrvMailbox . data_len > 0 ) {
char * p ;
uint8_t i = 0 ;
uint8_t parm [ 3 ] = { 0 } ;
for ( char * str = strtok_r ( XdrvMailbox . data , " , " , & p ) ; str & & i < 2 ; str = strtok_r ( nullptr , " , " , & p ) ) {
parm [ i ] = strtoul ( str , nullptr , 0 ) ;
i + + ;
}
2020-11-28 14:49:04 +00:00
if ( ( parm [ 0 ] > = 1 & & parm [ 0 ] < = 4 ) & & ( parm [ 1 ] > = 1 & & parm [ 1 ] < = 31 ) ) {
2020-11-07 21:08:07 +00:00
uint16_t idx = parm [ 0 ] + 230 ; // fnid 231, 232, 233 and 234 are reserved for enum
2021-06-11 17:14:12 +01:00
Settings - > tuya_fnid_map [ idx ] . fnid = idx ;
Settings - > tuya_fnid_map [ idx ] . dpid = parm [ 1 ] ;
2020-11-07 21:08:07 +00:00
}
}
if ( ( TuyaGetDpId ( TUYA_MCU_FUNC_ENUM1 ) ! = 0 ) | | ( TuyaGetDpId ( TUYA_MCU_FUNC_ENUM3 ) ! = 0 ) | |
( TuyaGetDpId ( TUYA_MCU_FUNC_ENUM3 ) ! = 0 ) | | ( TuyaGetDpId ( TUYA_MCU_FUNC_ENUM4 ) ! = 0 ) ) {
2020-11-08 11:51:08 +00:00
Response_P ( PSTR ( " { \" %s \" :{ " ) , XdrvMailbox . command ) ; // Builds TuyaEnumList
2020-11-07 21:08:07 +00:00
bool added = false ;
for ( uint8_t i = 0 ; i < = 3 ; i + + ) {
if ( TuyaGetDpId ( TUYA_MCU_FUNC_ENUM1 + i ) ! = 0 ) {
if ( added ) {
ResponseAppend_P ( PSTR ( " , " ) ) ;
2021-06-11 17:14:12 +01:00
if ( Settings - > tuya_fnid_map [ i + 231 ] . dpid > 31 ) { Settings - > tuya_fnid_map [ i + 231 ] . dpid = 0 ; } // default to 0 it the value exceed the range
2020-11-07 21:08:07 +00:00
}
2021-06-11 17:14:12 +01:00
ResponseAppend_P ( PSTR ( " \" Enum%d \" :%d " ) , i + 1 , Settings - > tuya_fnid_map [ i + 231 ] . dpid ) ; // fnid 231, 232, 233 and 234 are reserved for Enum
2020-11-07 21:08:07 +00:00
added = true ;
}
}
2020-11-07 21:40:43 +00:00
ResponseAppend_P ( PSTR ( " }} " ) ) ;
2020-11-07 21:08:07 +00:00
} else { return ; }
}
2020-11-28 14:49:04 +00:00
int StrCmpNoCase ( char const * Str1 , char const * Str2 ) // Compare case sensistive RGB strings
2020-11-07 21:08:07 +00:00
{
for ( ; ; Str1 + + , Str2 + + ) {
int StrCmp = tolower ( ( unsigned char ) * Str1 ) - tolower ( ( unsigned char ) * Str2 ) ;
if ( StrCmp ! = 0 | | ! * Str1 ) { return StrCmp ; }
}
}
2021-02-18 15:34:33 +00:00
float TuyaAdjustedTemperature ( int16_t packetValue , uint8_t res )
{
switch ( res )
{
case 1 :
return ( float ) packetValue / 10.0 ;
break ;
case 2 :
return ( float ) packetValue / 100.0 ;
break ;
case 3 :
return ( float ) packetValue / 1000.0 ;
2021-02-28 11:50:02 +00:00
break ;
2021-02-18 15:34:33 +00:00
default :
return ( float ) packetValue ;
break ;
2021-02-28 11:50:02 +00:00
}
2021-02-18 15:34:33 +00:00
}
2021-04-19 11:25:53 +01:00
2018-11-04 05:56:36 +00:00
/*********************************************************************************************\
* Internal Functions
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-09-03 11:23:44 +01:00
void TuyaAddMcuFunc ( uint8_t fnId , uint8_t dpId ) {
bool added = false ;
if ( fnId = = 0 | | dpId = = 0 ) { // Delete entry
for ( uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS ; i + + ) {
2021-06-11 17:14:12 +01:00
if ( ( dpId > 0 & & Settings - > tuya_fnid_map [ i ] . dpid = = dpId ) | | ( fnId > TUYA_MCU_FUNC_NONE & & Settings - > tuya_fnid_map [ i ] . fnid = = fnId ) ) {
Settings - > tuya_fnid_map [ i ] . fnid = TUYA_MCU_FUNC_NONE ;
Settings - > tuya_fnid_map [ i ] . dpid = 0 ;
2019-09-03 11:23:44 +01:00
break ;
}
}
} else { // Add or update
for ( uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS ; i + + ) {
2021-06-11 17:14:12 +01:00
if ( Settings - > tuya_fnid_map [ i ] . dpid = = dpId | | Settings - > tuya_fnid_map [ i ] . dpid = = 0 | | Settings - > tuya_fnid_map [ i ] . fnid = = fnId | | Settings - > tuya_fnid_map [ i ] . fnid = = 0 ) {
2019-09-03 11:23:44 +01:00
if ( ! added ) { // Update entry if exisiting entry or add
2021-06-11 17:14:12 +01:00
Settings - > tuya_fnid_map [ i ] . fnid = fnId ;
Settings - > tuya_fnid_map [ i ] . dpid = dpId ;
2019-09-03 11:23:44 +01:00
added = true ;
2021-06-11 17:14:12 +01:00
} else if ( Settings - > tuya_fnid_map [ i ] . dpid = = dpId | | Settings - > tuya_fnid_map [ i ] . fnid = = fnId ) { // Remove existing entry if added to empty place
Settings - > tuya_fnid_map [ i ] . fnid = TUYA_MCU_FUNC_NONE ;
Settings - > tuya_fnid_map [ i ] . dpid = 0 ;
2019-09-03 11:23:44 +01:00
}
}
}
}
2019-09-03 14:04:07 +01:00
UpdateDevices ( ) ;
}
void UpdateDevices ( ) {
for ( uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS ; i + + ) {
2021-06-11 17:14:12 +01:00
uint8_t fnId = Settings - > tuya_fnid_map [ i ] . fnid ;
if ( fnId > TUYA_MCU_FUNC_NONE & & Settings - > tuya_fnid_map [ i ] . dpid > 0 ) {
2019-09-03 14:04:07 +01:00
if ( fnId > = TUYA_MCU_FUNC_REL1 & & fnId < = TUYA_MCU_FUNC_REL8 ) { //Relay
2020-10-28 18:03:39 +00:00
bitClear ( TasmotaGlobal . rel_inverted , fnId - TUYA_MCU_FUNC_REL1 ) ;
2019-09-03 14:04:07 +01:00
} else if ( fnId > = TUYA_MCU_FUNC_REL1_INV & & fnId < = TUYA_MCU_FUNC_REL8_INV ) { // Inverted Relay
2020-10-28 18:03:39 +00:00
bitSet ( TasmotaGlobal . rel_inverted , fnId - TUYA_MCU_FUNC_REL1_INV ) ;
2019-09-03 14:04:07 +01:00
}
}
}
2019-09-03 11:23:44 +01:00
}
inline bool TuyaFuncIdValid ( uint8_t fnId ) {
2019-09-03 11:57:13 +01:00
return ( fnId > = TUYA_MCU_FUNC_SWT1 & & fnId < = TUYA_MCU_FUNC_SWT4 ) | |
2020-09-16 19:45:56 +01:00
( fnId > = TUYA_MCU_FUNC_REL1 & & fnId < = TUYA_MCU_FUNC_REL8 ) | |
( fnId > = TUYA_MCU_FUNC_DIMMER & & fnId < = TUYA_MCU_FUNC_REPORT2 ) | |
2021-03-02 01:31:06 +00:00
( fnId > = TUYA_MCU_FUNC_POWER & & fnId < = TUYA_MCU_FUNC_POWER_TOTAL ) | |
2020-09-16 19:45:56 +01:00
( fnId > = TUYA_MCU_FUNC_REL1_INV & & fnId < = TUYA_MCU_FUNC_REL8_INV ) | |
2020-11-07 21:08:07 +00:00
( fnId > = TUYA_MCU_FUNC_ENUM1 & & fnId < = TUYA_MCU_FUNC_ENUM4 ) | |
2020-09-16 19:45:56 +01:00
( fnId > = TUYA_MCU_FUNC_MOTOR_DIR & & fnId < = TUYA_MCU_FUNC_DUMMY ) | |
2020-11-28 14:49:04 +00:00
( fnId = = TUYA_MCU_FUNC_LOWPOWER_MODE ) | |
( fnId > = TUYA_MCU_FUNC_TEMP & & fnId < = TUYA_MCU_FUNC_HUMSET ) | |
2022-08-14 23:58:45 +01:00
( fnId > = TUYA_MCU_FUNC_LX & & fnId < = TUYA_MCU_FUNC_PM25 ) | |
2020-11-28 14:49:04 +00:00
( fnId > = TUYA_MCU_FUNC_TIMER1 & & fnId < = TUYA_MCU_FUNC_TIMER4 ) ;
2019-09-03 11:23:44 +01:00
}
uint8_t TuyaGetFuncId ( uint8_t dpid ) {
for ( uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS ; i + + ) {
2021-06-11 17:14:12 +01:00
if ( Settings - > tuya_fnid_map [ i ] . dpid = = dpid ) {
return Settings - > tuya_fnid_map [ i ] . fnid ;
2019-09-03 11:23:44 +01:00
}
}
return TUYA_MCU_FUNC_NONE ;
}
uint8_t TuyaGetDpId ( uint8_t fnId ) {
for ( uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS ; i + + ) {
2021-06-11 17:14:12 +01:00
if ( Settings - > tuya_fnid_map [ i ] . fnid = = fnId ) {
return Settings - > tuya_fnid_map [ i ] . dpid ;
2019-09-03 11:23:44 +01:00
}
}
return 0 ;
}
2019-07-02 14:49:08 +01:00
void TuyaSendCmd ( uint8_t cmd , uint8_t payload [ ] = nullptr , uint16_t payload_len = 0 )
{
2018-11-04 02:40:14 +00:00
uint8_t checksum = ( 0xFF + cmd + ( payload_len > > 8 ) + ( payload_len & 0xFF ) ) ;
2018-11-04 05:40:55 +00:00
TuyaSerial - > write ( 0x55 ) ; // Tuya header 55AA
TuyaSerial - > write ( 0xAA ) ;
2018-11-04 05:56:36 +00:00
TuyaSerial - > write ( ( uint8_t ) 0x00 ) ; // version 00
2018-11-04 05:40:55 +00:00
TuyaSerial - > write ( cmd ) ; // Tuya command
TuyaSerial - > write ( payload_len > > 8 ) ; // following data length (Hi)
TuyaSerial - > write ( payload_len & 0xFF ) ; // following data length (Lo)
2021-06-08 17:46:39 +01:00
char log_data [ 700 ] ; // Was MAX_LOGSZ
2020-12-23 17:00:59 +00:00
snprintf_P ( log_data , sizeof ( log_data ) , PSTR ( " TYA: Send \" 55aa00%02x%02x%02x " ) , cmd , payload_len > > 8 , payload_len & 0xFF ) ;
2019-06-30 15:44:36 +01:00
for ( uint32_t i = 0 ; i < payload_len ; + + i ) {
2018-11-03 21:34:29 +00:00
TuyaSerial - > write ( payload [ i ] ) ;
checksum + = payload [ i ] ;
2020-12-23 17:00:59 +00:00
snprintf_P ( log_data , sizeof ( log_data ) , PSTR ( " %s%02x " ) , log_data , payload [ i ] ) ;
2018-11-03 21:34:29 +00:00
}
TuyaSerial - > write ( checksum ) ;
TuyaSerial - > flush ( ) ;
2020-12-23 17:00:59 +00:00
snprintf_P ( log_data , sizeof ( log_data ) , PSTR ( " %s%02x \" " ) , log_data , checksum ) ;
AddLogData ( LOG_LEVEL_DEBUG , log_data ) ;
2018-11-03 21:34:29 +00:00
}
2019-07-02 14:49:08 +01:00
void TuyaSendState ( uint8_t id , uint8_t type , uint8_t * value )
{
2018-11-03 21:34:29 +00:00
uint16_t payload_len = 4 ;
uint8_t payload_buffer [ 8 ] ;
payload_buffer [ 0 ] = id ;
payload_buffer [ 1 ] = type ;
2019-07-02 14:49:08 +01:00
switch ( type ) {
2018-11-03 21:34:29 +00:00
case TUYA_TYPE_BOOL :
2019-10-22 17:41:31 +01:00
case TUYA_TYPE_ENUM :
2018-11-03 21:34:29 +00:00
payload_len + = 1 ;
payload_buffer [ 2 ] = 0x00 ;
payload_buffer [ 3 ] = 0x01 ;
payload_buffer [ 4 ] = value [ 0 ] ;
break ;
case TUYA_TYPE_VALUE :
payload_len + = 4 ;
payload_buffer [ 2 ] = 0x00 ;
payload_buffer [ 3 ] = 0x04 ;
payload_buffer [ 4 ] = value [ 3 ] ;
payload_buffer [ 5 ] = value [ 2 ] ;
payload_buffer [ 6 ] = value [ 1 ] ;
payload_buffer [ 7 ] = value [ 0 ] ;
break ;
2019-10-22 17:41:31 +01:00
2018-11-03 21:34:29 +00:00
}
TuyaSendCmd ( TUYA_CMD_SET_DP , payload_buffer , payload_len ) ;
}
2019-07-02 14:49:08 +01:00
void TuyaSendBool ( uint8_t id , bool value )
{
TuyaSendState ( id , TUYA_TYPE_BOOL , ( uint8_t * ) & value ) ;
2018-11-03 21:34:29 +00:00
}
2019-07-02 14:49:08 +01:00
void TuyaSendValue ( uint8_t id , uint32_t value )
{
TuyaSendState ( id , TUYA_TYPE_VALUE , ( uint8_t * ) ( & value ) ) ;
2018-11-03 21:34:29 +00:00
}
2019-10-22 17:41:31 +01:00
void TuyaSendEnum ( uint8_t id , uint32_t value )
{
TuyaSendState ( id , TUYA_TYPE_ENUM , ( uint8_t * ) ( & value ) ) ;
}
2022-03-29 23:08:27 +01:00
static uint16_t convertHexStringtoBytes ( uint8_t * dest , char src [ ] , uint16_t src_len ) {
if ( NULL = = dest | | NULL = = src | | 0 = = src_len ) {
return 0 ;
}
2022-08-20 12:46:31 +01:00
2022-03-29 23:08:27 +01:00
char hexbyte [ 3 ] ;
hexbyte [ 2 ] = 0 ;
uint16_t i ;
for ( i = 0 ; i < src_len ; i + + ) {
hexbyte [ 0 ] = src [ 2 * i ] ;
hexbyte [ 1 ] = src [ 2 * i + 1 ] ;
dest [ i ] = strtol ( hexbyte , NULL , 16 ) ;
}
return i ;
}
2021-05-27 21:27:08 +01:00
void TuyaSendHexString ( uint8_t id , char data [ ] ) {
uint16_t len = strlen ( data ) / 2 ;
uint16_t payload_len = 4 + len ;
uint8_t payload_buffer [ payload_len ] ;
payload_buffer [ 0 ] = id ;
payload_buffer [ 1 ] = TUYA_TYPE_STRING ;
payload_buffer [ 2 ] = len > > 8 ;
payload_buffer [ 3 ] = len & 0xFF ;
2022-03-29 23:08:27 +01:00
( void ) convertHexStringtoBytes ( & payload_buffer [ 4 ] , data , len ) ;
2021-05-27 21:27:08 +01:00
TuyaSendCmd ( TUYA_CMD_SET_DP , payload_buffer , payload_len ) ;
}
2019-10-22 17:41:31 +01:00
void TuyaSendString ( uint8_t id , char data [ ] ) {
uint16_t len = strlen ( data ) ;
uint16_t payload_len = 4 + len ;
uint8_t payload_buffer [ payload_len ] ;
payload_buffer [ 0 ] = id ;
payload_buffer [ 1 ] = TUYA_TYPE_STRING ;
payload_buffer [ 2 ] = len > > 8 ;
payload_buffer [ 3 ] = len & 0xFF ;
for ( uint16_t i = 0 ; i < len ; i + + ) {
payload_buffer [ 4 + i ] = data [ i ] ;
}
TuyaSendCmd ( TUYA_CMD_SET_DP , payload_buffer , payload_len ) ;
}
2022-03-29 23:08:27 +01:00
void TuyaSendRaw ( uint8_t id , char data [ ] ) {
char * beginPos = strchr ( data , ' x ' ) ;
if ( ! beginPos ) {
beginPos = strchr ( data , ' X ' ) ;
}
if ( ! beginPos ) {
beginPos = data ;
} else {
beginPos + = 1 ;
}
uint16_t strSize = strlen ( beginPos ) ;
uint16_t len = strSize / 2 ;
uint16_t payload_len = 4 + len ;
uint8_t payload_buffer [ payload_len ] ;
payload_buffer [ 0 ] = id ;
payload_buffer [ 1 ] = TUYA_TYPE_RAW ;
payload_buffer [ 2 ] = len > > 8 ;
payload_buffer [ 3 ] = len & 0xFF ;
( void ) convertHexStringtoBytes ( & payload_buffer [ 4 ] , beginPos , len ) ;
TuyaSendCmd ( TUYA_CMD_SET_DP , payload_buffer , payload_len ) ;
}
2019-01-28 13:08:33 +00:00
bool TuyaSetPower ( void )
2018-10-18 12:01:31 +01:00
{
2019-01-28 13:08:33 +00:00
bool status = false ;
2018-10-18 12:01:31 +01:00
uint8_t rpower = XdrvMailbox . index ;
int16_t source = XdrvMailbox . payload ;
2020-10-29 12:58:50 +00:00
uint8_t dpid = TuyaGetDpId ( TUYA_MCU_FUNC_REL1 + TasmotaGlobal . active_device - 1 ) ;
if ( dpid = = 0 ) dpid = TuyaGetDpId ( TUYA_MCU_FUNC_REL1_INV + TasmotaGlobal . active_device - 1 ) ;
2019-10-24 16:06:03 +01:00
2018-10-27 03:11:30 +01:00
if ( source ! = SRC_SWITCH & & TuyaSerial ) { // ignore to prevent loop from pushing state from faceplate interaction
2020-10-29 12:58:50 +00:00
TuyaSendBool ( dpid , bitRead ( rpower , TasmotaGlobal . active_device - 1 ) ^ bitRead ( TasmotaGlobal . rel_inverted , TasmotaGlobal . active_device - 1 ) ) ;
2020-03-05 23:51:22 +00:00
delay ( 20 ) ; // Hack when power is off and dimmer is set then both commands go too soon to Serial out.
2018-10-18 12:01:31 +01:00
status = true ;
}
return status ;
}
2019-01-28 13:08:33 +00:00
bool TuyaSetChannels ( void )
2018-12-17 16:34:55 +00:00
{
2020-09-16 19:45:56 +01:00
uint16_t hue , TuyaData ;
uint8_t sat , bri ;
uint8_t TuyaIdx = 0 ;
2020-11-07 21:08:07 +00:00
char hex_char [ 15 ] ;
2020-09-16 19:45:56 +01:00
bool noupd = false ;
bool LightMode = TuyaGetDpId ( TUYA_MCU_FUNC_MODESET ) ! = 0 ;
uint8_t idx = 0 ;
snprintf_P ( hex_char , sizeof ( hex_char ) , PSTR ( " 000000000000 " ) ) ;
2020-10-30 11:29:48 +00:00
if ( LT_SERIAL1 = = TasmotaGlobal . light_type ) {
2020-09-16 19:45:56 +01:00
Tuya . Snapshot [ 0 ] = light_state . getDimmer ( ) ;
}
2020-10-30 11:29:48 +00:00
if ( LT_SERIAL2 = = TasmotaGlobal . light_type | | LT_RGBWC = = TasmotaGlobal . light_type ) {
2020-09-16 19:45:56 +01:00
idx = 1 ;
2021-06-11 17:14:12 +01:00
if ( LT_SERIAL2 = = TasmotaGlobal . light_type & & Settings - > flag3 . pwm_multi_channels & & ( TuyaGetDpId ( TUYA_MCU_FUNC_DIMMER2 ) ! = 0 ) ) {
2020-09-16 19:45:56 +01:00
// Special setup for dual dimmer (like the MOES 2 Way Dimmer) emulating 2 PWM channels
Tuya . Snapshot [ 0 ] = changeUIntScale ( Light . current_color [ 0 ] , 0 , 255 , 0 , 100 ) ;
Tuya . Snapshot [ 1 ] = changeUIntScale ( Light . current_color [ 1 ] , 0 , 255 , 0 , 100 ) ;
} else { // CT Light or RGBWC
2020-12-29 18:58:38 +00:00
getCTRange ( & Tuya . CTMin , & Tuya . CTMax ) ; // SetOption82 - Reduce the CT range from 153..500 to 200..380 to accomodate with Alexa range
2020-09-16 19:45:56 +01:00
Tuya . Snapshot [ 0 ] = light_state . getDimmer ( ) ;
Tuya . Snapshot [ 1 ] = light_state . getCT ( ) ;
}
}
2020-10-30 11:29:48 +00:00
if ( LT_RGBW = = TasmotaGlobal . light_type ) {
2020-09-16 19:45:56 +01:00
idx = 1 ;
Tuya . Snapshot [ 0 ] = light_state . getDimmer ( 1 ) ;
Tuya . Snapshot [ 1 ] = light_state . getDimmer ( 2 ) ;
}
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . light_type > LT_BASIC ) {
2020-09-16 19:45:56 +01:00
2020-10-30 11:29:48 +00:00
if ( LT_RGB ! = TasmotaGlobal . light_type ) {
2020-09-16 19:45:56 +01:00
for ( uint8_t i = 0 ; i < = idx ; i + + ) {
if ( Tuya . Snapshot [ i ] ! = Tuya . Levels [ i ] ) {
if ( i = = 0 & & LightMode & & Tuya . ModeSet ) { noupd = true ; }
if ( ! noupd ) {
LightSerialDuty ( Tuya . Snapshot [ i ] , & hex_char [ 0 ] , i + 1 ) ;
2020-11-28 14:49:04 +00:00
//Tuya.Levels[i] = Tuya.Snapshot[i];
2020-09-16 19:45:56 +01:00
}
noupd = false ;
}
}
}
2020-10-30 11:29:48 +00:00
if ( TasmotaGlobal . light_type > = LT_RGB ) {
2020-09-19 12:26:29 +01:00
2020-11-07 21:08:07 +00:00
// There are two types of rgb format, configure the correct one using TuyaRGB command.
// The most common is 0HUE0SAT0BRI0 and the less common is RRGGBBFFFF6464 and sometimes both are case sensitive:
// 0 type 1 Uppercase - 00DF00DC0244
// 1 Type 1 Lowercase - 008003e8037a
// 2 Type 2 Uppercase - 00FF00FFFF6464
// 3 Type 2 Lowercase - 00e420ffff6464
2021-06-11 17:14:12 +01:00
uint8_t RGBType = Settings - > tuya_fnid_map [ 230 ] . dpid ; // Select the type of RGB payload
2020-11-07 21:08:07 +00:00
char scolor [ 7 ] ;
LightGetColor ( scolor , 1 ) ; // Always get the color in hex format
2020-09-16 19:45:56 +01:00
light_state . getHSB ( & hue , & sat , & bri ) ;
sat = changeUIntScale ( sat , 0 , 255 , 0 , 100 ) ;
bri = changeUIntScale ( bri , 0 , 255 , 0 , 100 ) ;
2020-11-07 21:08:07 +00:00
if ( ( RGBType > 1 & & ( StrCmpNoCase ( scolor , Tuya . RGBColor ) ! = 0 ) ) | | ( RGBType < = 1 & & ( ( hue ! = Tuya . Snapshot [ 2 ] ) | |
( sat ! = Tuya . Snapshot [ 3 ] ) | | ( bri ! = Tuya . Snapshot [ 4 ] ) ) ) ) {
2020-10-30 11:29:48 +00:00
if ( ( LightMode & & Tuya . ModeSet ) | | LT_RGB = = TasmotaGlobal . light_type ) {
2020-11-07 21:08:07 +00:00
if ( TuyaGetDpId ( TUYA_MCU_FUNC_RGB ) ! = 0 ) {
switch ( RGBType ) {
case 0 : // Uppercase Type 1 payload
snprintf_P ( hex_char , sizeof ( hex_char ) , PSTR ( " %04X%04X%04X " ) , hue , sat * 10 , bri * 10 ) ;
break ;
case 1 : // Lowercase Type 1 payload
snprintf_P ( hex_char , sizeof ( hex_char ) , PSTR ( " %04x%04x%04x " ) , hue , sat * 10 , bri * 10 ) ;
break ;
case 2 : // Uppercase Type 2 payload
snprintf_P ( hex_char , sizeof ( hex_char ) , PSTR ( " %sFFFF6464 " ) , scolor ) ;
break ;
case 3 : // Lowercase Type 2 payload
snprintf_P ( hex_char , sizeof ( hex_char ) , PSTR ( " %sffff6464 " ) , LowerCase ( scolor , scolor ) ) ;
break ;
}
memcpy_P ( Tuya . RGBColor , scolor , strlen ( scolor ) ) ;
Tuya . Snapshot [ 2 ] = hue ;
Tuya . Snapshot [ 3 ] = sat ;
Tuya . Snapshot [ 4 ] = bri ;
}
2020-09-16 19:45:56 +01:00
LightSerialDuty ( 0 , & hex_char [ 0 ] , 3 ) ;
}
}
}
}
2018-12-17 16:34:55 +00:00
return true ;
}
2020-09-16 19:45:56 +01:00
void LightSerialDuty ( uint16_t duty , char * hex_char , uint8_t TuyaIdx )
2018-10-18 12:01:31 +01:00
{
2019-09-03 11:23:44 +01:00
uint8_t dpid = TuyaGetDpId ( TUYA_MCU_FUNC_DIMMER ) ;
2020-09-16 19:45:56 +01:00
bool CTLight = false ;
if ( TuyaIdx > 0 & & TuyaIdx < = 2 ) {
if ( TuyaIdx = = 2 ) {
2021-06-11 17:14:12 +01:00
if ( ! Settings - > flag3 . pwm_multi_channels ) {
2020-09-16 19:45:56 +01:00
CTLight = true ;
dpid = TuyaGetDpId ( TUYA_MCU_FUNC_CT ) ;
} else { dpid = TuyaGetDpId ( TUYA_MCU_FUNC_DIMMER2 ) ; }
2019-07-02 14:49:08 +01:00
}
2021-02-28 11:50:02 +00:00
2021-02-24 23:28:34 +00:00
if ( Tuya . ignore_dim & & Tuya . ignore_dimmer_cmd_timeout < millis ( ) ) {
Tuya . ignore_dim = false ;
2021-02-28 11:50:02 +00:00
}
2020-09-16 19:45:56 +01:00
if ( duty > 0 & & ! Tuya . ignore_dim & & TuyaSerial & & dpid > 0 ) {
if ( TuyaIdx = = 2 & & CTLight ) {
2021-06-11 17:14:12 +01:00
duty = changeUIntScale ( duty , Tuya . CTMin , Tuya . CTMax , Settings - > dimmer_hw_max , 0 ) ;
2021-11-28 22:26:20 +00:00
} else { duty = changeUIntScale ( duty , 0 , 100 , Settings - > dimmer_hw_min , Settings - > dimmer_hw_max ) ; }
2020-09-16 19:45:56 +01:00
2021-06-11 17:14:12 +01:00
if ( duty < Settings - > dimmer_hw_min ) { duty = Settings - > dimmer_hw_min ; } // dimming acts odd below 25(10%) - this mirrors the threshold set on the faceplate itself
2020-09-16 19:45:56 +01:00
Tuya . ignore_dimmer_cmd_timeout = millis ( ) + 250 ; // Ignore serial received dim commands for the next 250ms
2020-10-30 11:29:48 +00:00
if ( Tuya . ModeSet & & ( TuyaGetDpId ( TUYA_MCU_FUNC_MODESET ) ! = 0 ) & & TasmotaGlobal . light_type > LT_RGB ) {
2020-09-16 19:45:56 +01:00
TuyaSendEnum ( TuyaGetDpId ( TUYA_MCU_FUNC_MODESET ) , 0 ) ;
}
TuyaSendValue ( dpid , duty ) ;
} else if ( dpid > 0 & & TuyaIdx < = 2 ) {
Tuya . ignore_dim = false ; // reset flag
if ( TuyaIdx = = 2 & & CTLight ) {
2021-06-11 17:14:12 +01:00
duty = changeUIntScale ( duty , Tuya . CTMin , Tuya . CTMax , Settings - > dimmer_hw_max , 0 ) ;
2020-09-16 19:45:56 +01:00
} else {
2021-11-28 22:26:20 +00:00
duty = changeUIntScale ( duty , 0 , 100 , Settings - > dimmer_hw_min , Settings - > dimmer_hw_max ) ;
2020-09-16 19:45:56 +01:00
}
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Send dim skipped value %d for dpid %d " ) , duty , dpid ) ; // due to 0 or already set
2020-09-16 19:45:56 +01:00
} else {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Cannot set dimmer. Dimmer Id unknown " ) ) ;
2020-09-16 19:45:56 +01:00
}
}
if ( TuyaIdx = = 3 ) {
dpid = TuyaGetDpId ( TUYA_MCU_FUNC_RGB ) ;
2020-10-30 11:29:48 +00:00
if ( ! Tuya . ModeSet & & ( TuyaGetDpId ( TUYA_MCU_FUNC_MODESET ) ! = 0 ) & & TasmotaGlobal . light_type > LT_RGB ) {
2020-09-16 19:45:56 +01:00
TuyaSendEnum ( TuyaGetDpId ( TUYA_MCU_FUNC_MODESET ) , 1 ) ;
}
TuyaSendString ( dpid , hex_char ) ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: TX RGB hex %s to dpId %d " ) , hex_char , dpid ) ;
2018-10-18 12:01:31 +01:00
}
}
2020-09-16 19:45:56 +01:00
void TuyaRequestState ( uint8_t state_type )
2019-07-02 14:49:08 +01:00
{
if ( TuyaSerial ) {
2018-11-04 05:56:36 +00:00
// Get current status of MCU
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Read MCU state " ) ) ;
2020-09-16 19:45:56 +01:00
Tuya . SuspendTopic = true ;
Tuya . ignore_topic_timeout = millis ( ) + 1000 ; // suppress /STAT topic for 1000ms to limit data
switch ( state_type ) {
case 0 :
TuyaSendCmd ( TUYA_CMD_QUERY_STATE ) ;
break ;
case 8 :
TuyaSendCmd ( TUYA_CMD_QUERY_PRODUCT ) ;
break ;
}
2018-11-04 05:56:36 +00:00
}
}
2018-11-14 13:32:09 +00:00
void TuyaResetWifi ( void )
2018-11-04 05:56:36 +00:00
{
2021-06-11 17:14:12 +01:00
if ( ! Settings - > flag . button_restrict ) { // SetOption1 - Control button multipress
2018-11-04 05:56:36 +00:00
char scmnd [ 20 ] ;
snprintf_P ( scmnd , sizeof ( scmnd ) , D_CMND_WIFICONFIG " %d " , 2 ) ;
ExecuteCommand ( scmnd , SRC_BUTTON ) ;
}
}
2019-10-14 14:20:47 +01:00
void TuyaProcessStatePacket ( void ) {
2018-10-18 12:01:31 +01:00
char scmnd [ 20 ] ;
2020-01-16 15:19:57 +00:00
uint8_t dpidStart = 6 ;
uint8_t fnId ;
uint16_t dpDataLen ;
2020-11-28 14:49:04 +00:00
bool PowerOff = false ;
2021-03-02 01:31:06 +00:00
bool tuya_energy_enabled = ( XNRG_32 = = TasmotaGlobal . energy_driver ) ;
2020-01-16 15:19:57 +00:00
while ( dpidStart + 4 < Tuya . byte_counter ) {
dpDataLen = Tuya . buffer [ dpidStart + 2 ] < < 8 | Tuya . buffer [ dpidStart + 3 ] ;
fnId = TuyaGetFuncId ( Tuya . buffer [ dpidStart ] ) ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: fnId=%d is set for dpId=%d " ) , fnId , Tuya . buffer [ dpidStart ] ) ;
2021-04-19 11:25:53 +01:00
if ( Tuya . buffer [ dpidStart + 1 ] = = 0 ) {
2021-03-02 01:31:06 +00:00
# ifdef USE_ENERGY_SENSOR
if ( tuya_energy_enabled & & fnId = = TUYA_MCU_FUNC_POWER_COMBINED ) {
2022-09-13 13:17:39 +01:00
if ( dpDataLen > = 8 ) {
2021-03-02 01:31:06 +00:00
uint16_t tmpVol = Tuya . buffer [ dpidStart + 4 ] < < 8 | Tuya . buffer [ dpidStart + 5 ] ;
uint16_t tmpCur = Tuya . buffer [ dpidStart + 7 ] < < 8 | Tuya . buffer [ dpidStart + 8 ] ;
uint16_t tmpPow = Tuya . buffer [ dpidStart + 10 ] < < 8 | Tuya . buffer [ dpidStart + 11 ] ;
Energy . voltage [ 0 ] = ( float ) tmpVol / 10 ;
Energy . current [ 0 ] = ( float ) tmpCur / 1000 ;
Energy . active_power [ 0 ] = ( float ) tmpPow ;
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Rx ID=%d Voltage=%d " ) , Tuya . buffer [ dpidStart ] , tmpVol ) ;
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Rx ID=%d Current=%d " ) , Tuya . buffer [ dpidStart ] , tmpCur ) ;
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Rx ID=%d Active_Power=%d " ) , Tuya . buffer [ dpidStart ] , tmpPow ) ;
if ( RtcTime . valid ) {
if ( Tuya . lastPowerCheckTime ! = 0 & & Energy . active_power [ 0 ] > 0 ) {
2021-09-29 14:33:58 +01:00
Energy . kWhtoday [ 0 ] + = Energy . active_power [ 0 ] * ( float ) ( Rtc . utc_time - Tuya . lastPowerCheckTime ) / 36.0 ;
2021-03-02 01:31:06 +00:00
EnergyUpdateToday ( ) ;
}
Tuya . lastPowerCheckTime = Rtc . utc_time ;
}
} else {
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Rx ID=%d INV_LEN=%d " ) , Tuya . buffer [ dpidStart ] , dpDataLen ) ;
}
}
# endif // USE_ENERGY_SENSOR
}
else if ( Tuya . buffer [ dpidStart + 1 ] = = 1 ) { // Data Type 1
2020-01-16 15:19:57 +00:00
if ( fnId > = TUYA_MCU_FUNC_REL1 & & fnId < = TUYA_MCU_FUNC_REL8 ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX Relay-%d --> MCU State: %s Current State:%s " ) , fnId - TUYA_MCU_FUNC_REL1 + 1 , Tuya . buffer [ dpidStart + 4 ] ? " On " : " Off " , bitRead ( TasmotaGlobal . power , fnId - TUYA_MCU_FUNC_REL1 ) ? " On " : " Off " ) ;
2021-11-28 22:26:20 +00:00
if ( Tuya . buffer [ dpidStart + 4 ] ! = bitRead ( TasmotaGlobal . power , fnId - TUYA_MCU_FUNC_REL1 ) ) {
2020-11-28 14:49:04 +00:00
if ( ! Tuya . buffer [ dpidStart + 4 ] ) { PowerOff = true ; }
2020-01-16 15:19:57 +00:00
ExecuteCommandPower ( fnId - TUYA_MCU_FUNC_REL1 + 1 , Tuya . buffer [ dpidStart + 4 ] , SRC_SWITCH ) ; // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction
}
} else if ( fnId > = TUYA_MCU_FUNC_REL1_INV & & fnId < = TUYA_MCU_FUNC_REL8_INV ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX Relay-%d-Inverted --> MCU State: %s Current State:%s " ) , fnId - TUYA_MCU_FUNC_REL1_INV + 1 , Tuya . buffer [ dpidStart + 4 ] ? " Off " : " On " , bitRead ( TasmotaGlobal . power , fnId - TUYA_MCU_FUNC_REL1_INV ) ^ 1 ? " Off " : " On " ) ;
2020-10-28 18:03:39 +00:00
if ( Tuya . buffer [ dpidStart + 4 ] ! = bitRead ( TasmotaGlobal . power , fnId - TUYA_MCU_FUNC_REL1_INV ) ^ 1 ) {
2020-01-16 15:19:57 +00:00
ExecuteCommandPower ( fnId - TUYA_MCU_FUNC_REL1_INV + 1 , Tuya . buffer [ dpidStart + 4 ] ^ 1 , SRC_SWITCH ) ; // send SRC_SWITCH? to use as flag to prevent loop from inbound states from faceplate interaction
2020-11-28 14:49:04 +00:00
if ( Tuya . buffer [ dpidStart + 4 ] ) { PowerOff = true ; }
2020-01-16 15:19:57 +00:00
}
} else if ( fnId > = TUYA_MCU_FUNC_SWT1 & & fnId < = TUYA_MCU_FUNC_SWT4 ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX Switch-%d --> MCU State: %d Current State:%d " ) , fnId - TUYA_MCU_FUNC_SWT1 + 1 , Tuya . buffer [ dpidStart + 4 ] , SwitchGetVirtual ( fnId - TUYA_MCU_FUNC_SWT1 ) ) ;
2019-10-14 14:20:47 +01:00
2020-01-16 15:19:57 +00:00
if ( SwitchGetVirtual ( fnId - TUYA_MCU_FUNC_SWT1 ) ! = Tuya . buffer [ dpidStart + 4 ] ) {
SwitchSetVirtual ( fnId - TUYA_MCU_FUNC_SWT1 , Tuya . buffer [ dpidStart + 4 ] ) ;
SwitchHandler ( 1 ) ;
}
2019-10-14 14:20:47 +01:00
}
2020-11-28 14:49:04 +00:00
if ( PowerOff ) { Tuya . ignore_dimmer_cmd_timeout = millis ( ) + 250 ; }
2019-10-14 14:20:47 +01:00
}
2020-01-16 15:19:57 +00:00
else if ( Tuya . buffer [ dpidStart + 1 ] = = 2 ) { // Data Type 2
2021-06-03 18:16:06 +01:00
uint32_t packetValue = Tuya . buffer [ dpidStart + 4 ] < < 24 | Tuya . buffer [ dpidStart + 5 ] < < 16 | Tuya . buffer [ dpidStart + 6 ] < < 8 | Tuya . buffer [ dpidStart + 7 ] ; // TYpe 2 is a 32 bit integer
2020-09-16 19:45:56 +01:00
uint8_t dimIndex ;
2020-11-28 14:49:04 +00:00
bool SnsUpdate = false ;
if ( ( fnId > = TUYA_MCU_FUNC_TEMP ) & & ( fnId < = TUYA_MCU_FUNC_TIMER4 ) ) { // Sensors start from fnId 71
if ( packetValue ! = Tuya . Sensors [ fnId - 71 ] ) {
Tuya . SensorsValid [ fnId - 71 ] = true ;
Tuya . Sensors [ fnId - 71 ] = packetValue ;
SnsUpdate = true ;
}
}
if ( SnsUpdate ) {
char sname [ 20 ] ;
char tempval [ 5 ] ;
uint8_t res ;
2022-03-26 13:45:13 +00:00
bool dont_publish = Settings - > flag5 . tuyasns_no_immediate ;
2020-11-28 14:49:04 +00:00
if ( TasmotaGlobal . uptime < 8 ) { // delay to avoid multiple topics at the same time at boot time
return ;
} else {
2022-03-26 13:45:13 +00:00
if ( fnId > 80 | | fnId = = 74 | | fnId = = 72 ) {
dont_publish = false ;
}
2020-11-28 14:49:04 +00:00
if ( fnId > 74 ) {
res = 0 ;
2021-04-27 10:43:53 +01:00
} else if ( fnId > 72 ) {
2021-06-11 17:14:12 +01:00
res = Settings - > flag2 . humidity_resolution ;
2021-04-19 11:25:53 +01:00
} else if ( fnId = = 72 ) {
2021-06-11 17:14:12 +01:00
res = Settings - > mbflag2 . temperature_set_res ;
2021-04-19 11:25:53 +01:00
} else {
2021-06-11 17:14:12 +01:00
res = Settings - > flag2 . temperature_resolution ;
2021-04-19 11:25:53 +01:00
}
2020-11-28 14:49:04 +00:00
GetTextIndexed ( sname , sizeof ( sname ) , ( fnId - 71 ) , kTuyaSensors ) ;
ResponseClear ( ) ; // Clear retained message
2021-02-18 15:34:33 +00:00
Response_P ( PSTR ( " { \" TuyaSNS \" :{ \" %s \" :%s}} " ) , sname , dtostrfd ( TuyaAdjustedTemperature ( packetValue , res ) , res , tempval ) ) ; // sensor update is just on change
2022-03-26 13:45:13 +00:00
if ( dont_publish ) {
2022-03-24 21:56:31 +00:00
XdrvRulesProcess ( 0 ) ;
} else {
MqttPublishPrefixTopicRulesProcess_P ( TELE , PSTR ( D_CMND_SENSOR ) ) ;
}
2020-11-28 14:49:04 +00:00
}
}
2020-09-16 19:45:56 +01:00
2020-10-22 20:10:52 +01:00
if ( fnId = = TUYA_MCU_FUNC_DIMMER | | fnId = = TUYA_MCU_FUNC_REPORT1 ) { dimIndex = 0 ; }
2020-09-16 19:45:56 +01:00
2020-10-22 20:10:52 +01:00
if ( fnId = = TUYA_MCU_FUNC_DIMMER2 | | fnId = = TUYA_MCU_FUNC_REPORT2 | | fnId = = TUYA_MCU_FUNC_CT ) { dimIndex = 1 ; }
2021-06-11 17:14:12 +01:00
if ( dimIndex = = 1 & & ! Settings - > flag3 . pwm_multi_channels ) {
Tuya . Levels [ 1 ] = changeUIntScale ( packetValue , 0 , Settings - > dimmer_hw_max , Tuya . CTMax , Tuya . CTMin ) ;
2020-10-22 20:10:52 +01:00
} else {
2021-11-28 22:26:20 +00:00
Tuya . Levels [ dimIndex ] = changeUIntScale ( packetValue , Settings - > dimmer_hw_min , Settings - > dimmer_hw_max , 0 , 100 ) ;
2020-09-16 19:45:56 +01:00
}
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX value %d from dpId %d " ) , packetValue , Tuya . buffer [ dpidStart ] ) ;
2020-09-16 19:45:56 +01:00
2020-10-17 11:48:45 +01:00
if ( ( fnId = = TUYA_MCU_FUNC_DIMMER ) | | ( fnId = = TUYA_MCU_FUNC_REPORT1 ) | |
( fnId = = TUYA_MCU_FUNC_DIMMER2 ) | | ( fnId = = TUYA_MCU_FUNC_REPORT2 ) | |
( fnId = = TUYA_MCU_FUNC_CT ) | | ( fnId = = TUYA_MCU_FUNC_WHITE ) ) {
2020-09-16 19:45:56 +01:00
2020-10-17 11:48:45 +01:00
if ( Tuya . ignore_dimmer_cmd_timeout < millis ( ) ) {
2021-11-29 21:46:31 +00:00
if ( ( TasmotaGlobal . power | | Settings - > flag3 . tuya_apply_o20 ) & & ( ( Tuya . Levels [ dimIndex ] > 0 | | Settings - > flag5 . tuya_allow_dimmer_0 ) & & ( Tuya . Levels [ dimIndex ] ! = Tuya . Snapshot [ dimIndex ] ) ) ) { // SetOption54 - Apply SetOption20 settings to Tuya device / SetOption131 Allow save dimmer = 0 receved by MCU
2020-10-17 11:48:45 +01:00
Tuya . ignore_dim = true ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . skip_light_fade = true ;
2020-09-16 19:45:56 +01:00
2020-10-17 11:48:45 +01:00
scmnd [ 0 ] = ' \0 ' ;
if ( ( fnId = = TUYA_MCU_FUNC_DIMMER ) | | ( fnId = = TUYA_MCU_FUNC_REPORT1 ) ) {
2021-06-11 17:14:12 +01:00
if ( Settings - > flag3 . pwm_multi_channels & & ( abs ( Tuya . Levels [ 0 ] - changeUIntScale ( Light . current_color [ 0 ] , 0 , 255 , 0 , 100 ) ) ) > 1 ) {
2020-10-17 11:48:45 +01:00
snprintf_P ( scmnd , sizeof ( scmnd ) , PSTR ( D_CMND_CHANNEL " 1 %d " ) , Tuya . Levels [ 0 ] ) ;
}
else if ( ( abs ( Tuya . Levels [ 0 ] - light_state . getDimmer ( ) ) ) > 1 ) {
snprintf_P ( scmnd , sizeof ( scmnd ) , PSTR ( D_CMND_DIMMER " 3 %d " ) , Tuya . Levels [ 0 ] ) ;
}
}
if ( ( ( fnId = = TUYA_MCU_FUNC_DIMMER2 ) | | ( fnId = = TUYA_MCU_FUNC_REPORT2 ) ) & &
2021-06-11 17:14:12 +01:00
Settings - > flag3 . pwm_multi_channels & & ( abs ( Tuya . Levels [ 1 ] - changeUIntScale ( Light . current_color [ 1 ] , 0 , 255 , 0 , 100 ) ) ) > 1 ) {
2020-10-17 11:48:45 +01:00
snprintf_P ( scmnd , sizeof ( scmnd ) , PSTR ( D_CMND_CHANNEL " 2 %d " ) , Tuya . Levels [ 1 ] ) ;
}
if ( ( fnId = = TUYA_MCU_FUNC_CT ) & & ( abs ( Tuya . Levels [ 1 ] - light_state . getCT ( ) ) ) > 1 ) {
snprintf_P ( scmnd , sizeof ( scmnd ) , PSTR ( D_CMND_COLORTEMPERATURE " %d " ) , Tuya . Levels [ 1 ] ) ;
}
if ( ( fnId = = TUYA_MCU_FUNC_WHITE ) & & ( abs ( Tuya . Levels [ 1 ] - light_state . getDimmer ( 2 ) ) ) > 1 ) {
snprintf_P ( scmnd , sizeof ( scmnd ) , PSTR ( D_CMND_WHITE " %d " ) , Tuya . Levels [ 1 ] ) ;
}
if ( scmnd [ 0 ] ! = ' \0 ' ) {
ExecuteCommand ( scmnd , SRC_SWITCH ) ;
}
2020-03-05 23:51:22 +00:00
}
2020-11-28 14:49:04 +00:00
Tuya . Snapshot [ dimIndex ] = Tuya . Levels [ dimIndex ] ;
2020-01-16 15:19:57 +00:00
}
2019-10-14 14:20:47 +01:00
}
2020-01-16 15:19:57 +00:00
# ifdef USE_ENERGY_SENSOR
else if ( tuya_energy_enabled & & fnId = = TUYA_MCU_FUNC_VOLTAGE ) {
Energy . voltage [ 0 ] = ( float ) packetValue / 10 ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Rx ID=%d Voltage=%d " ) , Tuya . buffer [ dpidStart ] , packetValue ) ;
2020-01-16 15:19:57 +00:00
} else if ( tuya_energy_enabled & & fnId = = TUYA_MCU_FUNC_CURRENT ) {
Energy . current [ 0 ] = ( float ) packetValue / 1000 ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Rx ID=%d Current=%d " ) , Tuya . buffer [ dpidStart ] , packetValue ) ;
2020-01-16 15:19:57 +00:00
} else if ( tuya_energy_enabled & & fnId = = TUYA_MCU_FUNC_POWER ) {
Energy . active_power [ 0 ] = ( float ) packetValue / 10 ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Rx ID=%d Active_Power=%d " ) , Tuya . buffer [ dpidStart ] , packetValue ) ;
2020-01-16 15:19:57 +00:00
2020-11-29 12:32:13 +00:00
if ( RtcTime . valid ) {
if ( Tuya . lastPowerCheckTime ! = 0 & & Energy . active_power [ 0 ] > 0 ) {
2021-09-29 14:33:58 +01:00
Energy . kWhtoday [ 0 ] + = Energy . active_power [ 0 ] * ( float ) ( Rtc . utc_time - Tuya . lastPowerCheckTime ) / 36.0 ;
2020-11-29 12:32:13 +00:00
EnergyUpdateToday ( ) ;
}
Tuya . lastPowerCheckTime = Rtc . utc_time ;
2020-01-16 15:19:57 +00:00
}
2021-03-02 01:31:06 +00:00
} else if ( tuya_energy_enabled & & fnId = = TUYA_MCU_FUNC_POWER_TOTAL ) {
2021-10-02 17:29:05 +01:00
Energy . import_active [ 0 ] = ( float ) packetValue / 100 ;
2021-03-02 01:31:06 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Rx ID=%d Total_Power=%d " ) , Tuya . buffer [ dpidStart ] , packetValue ) ;
2021-10-02 17:29:05 +01:00
EnergyUpdateTotal ( ) ;
2019-10-14 14:20:47 +01:00
}
2020-01-16 15:19:57 +00:00
# endif // USE_ENERGY_SENSOR
}
2020-09-16 19:45:56 +01:00
else if ( Tuya . buffer [ dpidStart + 1 ] = = 3 ) { // Data Type 3
const unsigned char * dpData = ( unsigned char * ) & Tuya . buffer [ dpidStart + 4 ] ;
2020-11-07 21:08:07 +00:00
if ( ( TuyaGetDpId ( TUYA_MCU_FUNC_RGB ) ! = 0 ) ) {
2020-09-16 19:45:56 +01:00
2021-06-11 17:14:12 +01:00
uint8_t RGBType = Settings - > tuya_fnid_map [ 230 ] . dpid ; // Select the type of hex configured
2020-11-07 21:08:07 +00:00
char RgbData [ 15 ] ;
char RGB [ 7 ] ;
2020-09-16 19:45:56 +01:00
char HSB1 [ 5 ] , HSB2 [ 5 ] , HSB3 [ 5 ] ;
2020-11-07 21:08:07 +00:00
scmnd [ 0 ] = ' \0 ' ;
snprintf_P ( RgbData , sizeof ( RgbData ) , PSTR ( " %.*s " ) , dpDataLen , dpData ) ;
2020-09-16 19:45:56 +01:00
2020-11-07 21:08:07 +00:00
if ( RGBType < = 1 & & dpDataLen = = 12 ) {
snprintf_P ( HSB1 , sizeof ( HSB1 ) , PSTR ( " %.4s \n " ) , & RgbData [ 0 ] ) ;
snprintf_P ( HSB2 , sizeof ( HSB2 ) , PSTR ( " %.4s \n " ) , & RgbData [ 4 ] ) ;
snprintf_P ( HSB3 , sizeof ( HSB3 ) , PSTR ( " %.4s \n " ) , & RgbData [ 8 ] ) ;
if ( ( Tuya . Snapshot [ 2 ] ! = ( ( int ) strtol ( HSB1 , NULL , 16 ) ) | |
Tuya . Snapshot [ 3 ] ! = ( ( int ) strtol ( HSB2 , NULL , 16 ) ) / 10 | | Tuya . Snapshot [ 4 ] ! = ( ( int ) strtol ( HSB3 , NULL , 16 ) ) / 10 ) ) {
Tuya . Snapshot [ 2 ] = ( ( int ) strtol ( HSB1 , NULL , 16 ) ) ;
Tuya . Snapshot [ 3 ] = ( ( int ) strtol ( HSB2 , NULL , 16 ) ) / 10 ;
Tuya . Snapshot [ 4 ] = ( ( int ) strtol ( HSB3 , NULL , 16 ) ) / 10 ;
2020-09-16 19:45:56 +01:00
snprintf_P ( scmnd , sizeof ( scmnd ) , PSTR ( D_CMND_HSBCOLOR " %d,%d,%d " ) , ( ( int ) strtol ( HSB1 , NULL , 16 ) ) ,
( ( int ) strtol ( HSB2 , NULL , 16 ) ) / 10 , ( ( int ) strtol ( HSB3 , NULL , 16 ) ) / 10 ) ;
2020-11-07 21:08:07 +00:00
}
}
if ( RGBType > 1 & & dpDataLen = = 14 ) {
snprintf_P ( RGB , sizeof ( RGB ) , PSTR ( " %.6s \n " ) , & RgbData [ 0 ] ) ;
if ( StrCmpNoCase ( RGB , Tuya . RGBColor ) ! = 0 ) {
snprintf_P ( scmnd , sizeof ( scmnd ) , PSTR ( D_CMND_COLOR " %s " ) , RGB ) ;
memcpy_P ( Tuya . RGBColor , RGB , strlen ( RGB ) ) ;
}
}
if ( scmnd [ 0 ] ! = ' \0 ' ) {
ExecuteCommand ( scmnd , SRC_SWITCH ) ;
2020-09-16 19:45:56 +01:00
}
}
2021-04-19 11:25:53 +01:00
2020-09-16 19:45:56 +01:00
}
else if ( Tuya . buffer [ dpidStart + 1 ] = = 4 ) { // Data Type 4
const unsigned char * dpData = ( unsigned char * ) & Tuya . buffer [ dpidStart + 4 ] ;
2020-11-07 21:08:07 +00:00
if ( ( fnId = = TUYA_MCU_FUNC_MODESET ) ) { // Toggle light type
2020-09-16 19:45:56 +01:00
Tuya . ModeSet = dpData [ 0 ] ;
Tuya . Levels [ 3 ] = dpData [ 0 ] ;
}
2020-11-07 21:08:07 +00:00
if ( ( fnId > = TUYA_MCU_FUNC_ENUM1 ) & & ( fnId < = TUYA_MCU_FUNC_ENUM4 ) ) {
for ( uint8_t i = 0 ; i < = 3 ; i + + ) {
bool noupdate = false ;
if ( ( TUYA_MCU_FUNC_ENUM1 + i ) = = fnId ) {
if ( Tuya . EnumState [ i ] ! = dpData [ 0 ] ) {
Tuya . EnumState [ i ] = dpData [ 0 ] ;
2020-11-08 11:51:08 +00:00
snprintf_P ( scmnd , sizeof ( scmnd ) , PSTR ( D_PRFX_TUYA D_CMND_TUYA_ENUM " %d %d " ) , i + 1 , dpData [ 0 ] ) ;
2020-11-07 21:08:07 +00:00
ExecuteCommand ( scmnd , SRC_SWITCH ) ;
}
}
}
}
2020-09-16 19:45:56 +01:00
}
dpidStart + = dpDataLen + 4 ;
2020-01-16 15:19:57 +00:00
}
2019-10-14 14:20:47 +01:00
}
void TuyaLowPowerModePacketProcess ( void ) {
2019-08-17 14:00:57 +01:00
switch ( Tuya . buffer [ 3 ] ) {
2019-10-14 14:20:47 +01:00
case TUYA_CMD_QUERY_PRODUCT :
TuyaHandleProductInfoPacket ( ) ;
TuyaSetWifiLed ( ) ;
break ;
case TUYA_LOW_POWER_CMD_STATE :
TuyaProcessStatePacket ( ) ;
Tuya . send_success_next_second = true ;
break ;
}
}
void TuyaHandleProductInfoPacket ( void ) {
uint16_t dataLength = Tuya . buffer [ 4 ] < < 8 | Tuya . buffer [ 5 ] ;
char * data = & Tuya . buffer [ 6 ] ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " TYA: MCU Product ID: %.*s " ) , dataLength , data ) ;
2019-10-14 14:20:47 +01:00
}
void TuyaSendLowPowerSuccessIfNeeded ( void ) {
uint8_t success = 1 ;
if ( Tuya . send_success_next_second ) {
TuyaSendCmd ( TUYA_LOW_POWER_CMD_STATE , & success , 1 ) ;
Tuya . send_success_next_second = false ;
}
}
void TuyaNormalPowerModePacketProcess ( void )
{
switch ( Tuya . buffer [ 3 ] ) {
case TUYA_CMD_QUERY_PRODUCT :
TuyaHandleProductInfoPacket ( ) ;
TuyaSendCmd ( TUYA_CMD_MCU_CONF ) ;
break ;
2018-10-18 12:01:31 +01:00
2018-11-07 09:30:03 +00:00
case TUYA_CMD_HEARTBEAT :
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Heartbeat " ) ) ;
2019-08-17 14:00:57 +01:00
if ( Tuya . buffer [ 6 ] = = 0 ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Detected MCU restart " ) ) ;
2019-08-17 14:00:57 +01:00
Tuya . wifi_state = - 2 ;
2018-11-03 21:34:29 +00:00
}
break ;
2018-10-18 12:01:31 +01:00
2018-11-03 21:34:29 +00:00
case TUYA_CMD_STATE :
2019-10-14 14:20:47 +01:00
TuyaProcessStatePacket ( ) ;
2018-11-03 21:34:29 +00:00
break ;
case TUYA_CMD_WIFI_RESET :
case TUYA_CMD_WIFI_SELECT :
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX WiFi Reset " ) ) ;
2018-11-03 21:34:29 +00:00
TuyaResetWifi ( ) ;
break ;
case TUYA_CMD_WIFI_STATE :
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX WiFi LED set ACK " ) ) ;
2019-10-14 14:20:47 +01:00
Tuya . wifi_state = TuyaGetTuyaWifiState ( ) ;
2018-11-03 21:34:29 +00:00
break ;
case TUYA_CMD_MCU_CONF :
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX MCU configuration Mode=%d " ) , Tuya . buffer [ 5 ] ) ;
2018-11-03 21:34:29 +00:00
2019-09-03 11:23:44 +01:00
if ( Tuya . buffer [ 5 ] = = 2 ) { // Processing by ESP module mode
2019-08-17 14:00:57 +01:00
uint8_t led1_gpio = Tuya . buffer [ 6 ] ;
uint8_t key1_gpio = Tuya . buffer [ 7 ] ;
2019-01-28 13:08:33 +00:00
bool key1_set = false ;
bool led1_set = false ;
2021-06-11 17:14:12 +01:00
for ( uint32_t i = 0 ; i < nitems ( Settings - > my_gp . io ) ; i + + ) {
if ( Settings - > my_gp . io [ i ] = = AGPIO ( GPIO_LED1 ) ) led1_set = true ;
else if ( Settings - > my_gp . io [ i ] = = AGPIO ( GPIO_KEY1 ) ) key1_set = true ;
2018-11-03 21:34:29 +00:00
}
2021-06-11 17:14:12 +01:00
if ( ! Settings - > my_gp . io [ led1_gpio ] & & ! led1_set ) {
Settings - > my_gp . io [ led1_gpio ] = AGPIO ( GPIO_LED1 ) ;
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag = 2 ;
2018-11-03 21:34:29 +00:00
}
2021-06-11 17:14:12 +01:00
if ( ! Settings - > my_gp . io [ key1_gpio ] & & ! key1_set ) {
Settings - > my_gp . io [ key1_gpio ] = AGPIO ( GPIO_KEY1 ) ;
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag = 2 ;
2018-11-03 21:34:29 +00:00
}
2018-11-01 01:55:16 +00:00
}
2020-09-16 19:45:56 +01:00
TuyaRequestState ( 0 ) ;
2018-11-03 21:34:29 +00:00
break ;
2021-01-06 14:19:44 +00:00
# ifdef USE_TUYA_TIME
case TUYA_CMD_SET_TIME :
TuyaSetTime ( ) ;
break ;
# endif
2018-11-04 02:40:14 +00:00
default :
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: RX unknown command " ) ) ;
2018-11-01 01:55:16 +00:00
}
2018-10-18 12:01:31 +01:00
}
2018-11-04 05:56:36 +00:00
/*********************************************************************************************\
* API Functions
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-12-19 15:41:10 +00:00
bool TuyaModuleSelected ( void ) {
# ifdef ESP8266
if ( TUYA_DIMMER ! = TasmotaGlobal . module_type ) { return false ; }
2020-04-27 11:54:07 +01:00
if ( ! PinUsed ( GPIO_TUYA_RX ) | | ! PinUsed ( GPIO_TUYA_TX ) ) { // fallback to hardware-serial if not explicitly selected
2020-06-20 16:58:21 +01:00
SetPin ( 1 , AGPIO ( GPIO_TUYA_TX ) ) ;
SetPin ( 3 , AGPIO ( GPIO_TUYA_RX ) ) ;
2021-06-11 17:14:12 +01:00
Settings - > my_gp . io [ 1 ] = AGPIO ( GPIO_TUYA_TX ) ;
Settings - > my_gp . io [ 3 ] = AGPIO ( GPIO_TUYA_RX ) ;
2020-10-29 11:21:24 +00:00
TasmotaGlobal . restart_flag = 2 ;
2018-11-04 05:56:36 +00:00
}
2021-12-19 15:41:10 +00:00
# endif
if ( ! PinUsed ( GPIO_TUYA_RX ) | | ! PinUsed ( GPIO_TUYA_TX ) ) { return false ; }
2019-09-03 11:23:44 +01:00
if ( TuyaGetDpId ( TUYA_MCU_FUNC_DIMMER ) = = 0 & & TUYA_DIMMER_ID > 0 ) {
TuyaAddMcuFunc ( TUYA_MCU_FUNC_DIMMER , TUYA_DIMMER_ID ) ;
}
2019-09-03 14:04:07 +01:00
bool relaySet = false ;
for ( uint8_t i = 0 ; i < MAX_TUYA_FUNCTIONS ; i + + ) {
2021-06-11 17:14:12 +01:00
if ( ( Settings - > tuya_fnid_map [ i ] . fnid > = TUYA_MCU_FUNC_REL1 & & Settings - > tuya_fnid_map [ i ] . fnid < = TUYA_MCU_FUNC_REL8 ) | |
( Settings - > tuya_fnid_map [ i ] . fnid > = TUYA_MCU_FUNC_REL1_INV & & Settings - > tuya_fnid_map [ i ] . fnid < = TUYA_MCU_FUNC_REL8_INV ) ) {
2019-09-03 14:04:07 +01:00
relaySet = true ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . devices_present + + ;
2019-09-03 14:04:07 +01:00
}
}
2020-09-16 19:45:56 +01:00
if ( ! relaySet & & TuyaGetDpId ( TUYA_MCU_FUNC_DUMMY ) = = 0 ) { //by default the first relay is created automatically the dummy let remove it if not needed
2019-09-03 11:23:44 +01:00
TuyaAddMcuFunc ( TUYA_MCU_FUNC_REL1 , 1 ) ;
2020-10-30 11:29:48 +00:00
TasmotaGlobal . devices_present + + ;
2019-09-03 11:23:44 +01:00
SettingsSaveAll ( ) ;
}
2020-09-16 19:45:56 +01:00
// Possible combinations for Lights:
// 0: NONE = LT_BASIC
// 1: DIMMER = LT_SERIAL1 - Common one channel dimmer
// 2: DIMMER, DIMMER2 = LT_SERIAL2 - Two channels dimmer (special setup used with SetOption68)
// 3: DIMMER, CT = LT_SERIAL2 - Dimmable light and White Color Temperature
// 4: DIMMER, RGB = LT_RGB - RGB Light
// 5: DIMMER, RGB, CT = LT_RGBWC - RGB LIght and White Color Temperature
// 6: DIMMER, RGB, WHITE = LT_RGBW - RGB LIght and White
2019-09-03 11:23:44 +01:00
if ( TuyaGetDpId ( TUYA_MCU_FUNC_DIMMER ) ! = 0 ) {
2020-09-16 19:45:56 +01:00
if ( TuyaGetDpId ( TUYA_MCU_FUNC_RGB ) ! = 0 ) {
if ( TuyaGetDpId ( TUYA_MCU_FUNC_CT ) ! = 0 ) {
2020-10-30 11:29:48 +00:00
TasmotaGlobal . light_type = LT_RGBWC ;
2020-09-16 19:45:56 +01:00
} else if ( TuyaGetDpId ( TUYA_MCU_FUNC_WHITE ) ! = 0 ) {
2020-10-30 11:29:48 +00:00
TasmotaGlobal . light_type = LT_RGBW ;
} else { TasmotaGlobal . light_type = LT_RGB ; }
2020-09-16 19:45:56 +01:00
} else if ( TuyaGetDpId ( TUYA_MCU_FUNC_CT ) ! = 0 | | TuyaGetDpId ( TUYA_MCU_FUNC_DIMMER2 ) ! = 0 ) {
if ( TuyaGetDpId ( TUYA_MCU_FUNC_RGB ) ! = 0 ) {
2020-10-30 11:29:48 +00:00
TasmotaGlobal . light_type = LT_RGBWC ;
} else { TasmotaGlobal . light_type = LT_SERIAL2 ; }
} else { TasmotaGlobal . light_type = LT_SERIAL1 ; }
2019-08-20 11:13:37 +01:00
} else {
2020-10-30 11:29:48 +00:00
TasmotaGlobal . light_type = LT_BASIC ;
2019-08-20 11:13:37 +01:00
}
2019-10-14 14:20:47 +01:00
if ( TuyaGetDpId ( TUYA_MCU_FUNC_LOWPOWER_MODE ) ! = 0 ) {
Tuya . low_power_mode = true ;
2021-06-11 17:14:12 +01:00
Settings - > flag3 . fast_power_cycle_disable = true ; // SetOption65 - Disable fast power cycle detection for device reset
2019-10-14 14:20:47 +01:00
}
2019-09-03 14:04:07 +01:00
UpdateDevices ( ) ;
2018-11-04 05:56:36 +00:00
return true ;
}
2021-12-19 15:41:10 +00:00
void TuyaInit ( void ) {
2020-06-29 15:01:55 +01:00
int baudrate = 9600 ;
2021-06-11 17:14:12 +01:00
if ( Settings - > flag4 . tuyamcu_baudrate ) { baudrate = 115200 ; } // SetOption97 - Set Baud rate for TuyaMCU serial communication (0 = 9600 or 1 = 115200)
2020-06-29 15:01:55 +01:00
2019-08-17 14:00:57 +01:00
Tuya . buffer = ( char * ) ( malloc ( TUYA_BUFFER_SIZE ) ) ;
if ( Tuya . buffer ! = nullptr ) {
2020-04-26 16:33:27 +01:00
TuyaSerial = new TasmotaSerial ( Pin ( GPIO_TUYA_RX ) , Pin ( GPIO_TUYA_TX ) , 2 ) ;
2020-06-29 15:01:55 +01:00
if ( TuyaSerial - > begin ( baudrate ) ) {
2018-11-27 10:55:54 +00:00
if ( TuyaSerial - > hardwareSerial ( ) ) { ClaimSerial ( ) ; }
// Get MCU Configuration
2020-09-16 19:45:56 +01:00
Tuya . SuspendTopic = true ;
Tuya . ignore_topic_timeout = millis ( ) + 1000 ; // suppress /STAT topic for 1000ms to avoid data overflow
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Request MCU configuration at %d bps " ) , baudrate ) ;
2020-11-28 14:49:04 +00:00
2021-12-19 15:41:10 +00:00
Tuya . heartbeat_timer = 0 ; // init heartbeat timer when dimmer init is done
return ;
2018-11-27 10:55:54 +00:00
}
2021-12-19 15:41:10 +00:00
free ( Tuya . buffer ) ;
2018-11-04 05:56:36 +00:00
}
2021-12-19 15:41:10 +00:00
Tuya . active = false ;
2018-11-04 05:56:36 +00:00
}
2018-11-14 13:32:09 +00:00
void TuyaSerialInput ( void )
2018-10-18 12:01:31 +01:00
{
2018-10-27 10:37:42 +01:00
while ( TuyaSerial - > available ( ) ) {
2018-10-18 12:01:31 +01:00
yield ( ) ;
2019-01-28 13:08:33 +00:00
uint8_t serial_in_byte = TuyaSerial - > read ( ) ;
2018-10-18 12:01:31 +01:00
if ( serial_in_byte = = 0x55 ) { // Start TUYA Packet
2019-08-17 14:00:57 +01:00
Tuya . cmd_status = 1 ;
Tuya . buffer [ Tuya . byte_counter + + ] = serial_in_byte ;
Tuya . cmd_checksum + = serial_in_byte ;
2018-10-18 12:01:31 +01:00
}
2019-08-17 14:00:57 +01:00
else if ( Tuya . cmd_status = = 1 & & serial_in_byte = = 0xAA ) { // Only packtes with header 0x55AA are valid
Tuya . cmd_status = 2 ;
2018-10-18 12:01:31 +01:00
2019-08-17 14:00:57 +01:00
Tuya . byte_counter = 0 ;
Tuya . buffer [ Tuya . byte_counter + + ] = 0x55 ;
Tuya . buffer [ Tuya . byte_counter + + ] = 0xAA ;
Tuya . cmd_checksum = 0xFF ;
2018-10-18 12:01:31 +01:00
}
2019-08-17 14:00:57 +01:00
else if ( Tuya . cmd_status = = 2 ) {
if ( Tuya . byte_counter = = 5 ) { // Get length of data
Tuya . cmd_status = 3 ;
Tuya . data_len = serial_in_byte ;
2018-10-18 12:01:31 +01:00
}
2019-08-17 14:00:57 +01:00
Tuya . cmd_checksum + = serial_in_byte ;
Tuya . buffer [ Tuya . byte_counter + + ] = serial_in_byte ;
2018-10-18 12:01:31 +01:00
}
2019-08-17 14:00:57 +01:00
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 ;
2018-10-18 12:01:31 +01:00
2019-10-21 17:24:03 +01:00
char hex_char [ ( Tuya . byte_counter * 2 ) + 2 ] ;
2019-10-24 16:03:07 +01:00
uint16_t len = Tuya . buffer [ 4 ] < < 8 | Tuya . buffer [ 5 ] ;
2020-09-16 19:45:56 +01:00
2019-10-24 16:03:07 +01:00
Response_P ( PSTR ( " { \" " D_JSON_TUYA_MCU_RECEIVED " \" :{ \" Data \" : \" %s \" , \" Cmnd \" :%d " ) , ToHex_P ( ( unsigned char * ) Tuya . buffer , Tuya . byte_counter , hex_char , sizeof ( hex_char ) ) , Tuya . buffer [ 3 ] ) ;
2020-09-16 19:45:56 +01:00
uint16_t DataVal = 0 ;
uint8_t dpId = 0 ;
uint8_t dpDataType = 0 ;
2020-11-07 21:08:07 +00:00
char DataStr [ 15 ] ;
2022-04-01 16:33:57 +01:00
bool isCmdToSuppress = false ;
2020-09-16 19:45:56 +01:00
2019-10-24 16:03:07 +01:00
if ( len > 0 ) {
ResponseAppend_P ( PSTR ( " , \" CmndData \" : \" %s \" " ) , ToHex_P ( ( unsigned char * ) & Tuya . buffer [ 6 ] , len , hex_char , sizeof ( hex_char ) ) ) ;
if ( TUYA_CMD_STATE = = Tuya . buffer [ 3 ] ) {
2020-01-16 15:19:57 +00:00
//55 AA 03 07 00 0D 01 04 00 01 02 02 02 00 04 00 00 00 1A 40
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
uint8_t dpidStart = 6 ;
while ( dpidStart + 4 < Tuya . byte_counter ) {
2020-09-16 19:45:56 +01:00
dpId = Tuya . buffer [ dpidStart ] ;
dpDataType = Tuya . buffer [ dpidStart + 1 ] ;
2020-01-16 15:19:57 +00:00
uint16_t dpDataLen = Tuya . buffer [ dpidStart + 2 ] < < 8 | Tuya . buffer [ dpidStart + 3 ] ;
2020-02-03 10:41:46 +00:00
const unsigned char * dpData = ( unsigned char * ) & Tuya . buffer [ dpidStart + 4 ] ;
const char * dpHexData = ToHex_P ( dpData , dpDataLen , hex_char , sizeof ( hex_char ) ) ;
2020-02-04 10:30:30 +00:00
if ( TUYA_CMD_STATE = = Tuya . buffer [ 3 ] ) {
ResponseAppend_P ( PSTR ( " , \" DpType%uId%u \" : " ) , dpDataType , dpId ) ;
if ( TUYA_TYPE_BOOL = = dpDataType & & dpDataLen = = 1 ) {
2020-02-19 07:10:07 +00:00
ResponseAppend_P ( PSTR ( " %u " ) , dpData [ 0 ] ) ;
2020-09-16 19:45:56 +01:00
DataVal = dpData [ 0 ] ;
2020-02-04 10:30:30 +00:00
} else if ( TUYA_TYPE_VALUE = = dpDataType & & dpDataLen = = 4 ) {
uint32_t dpValue = ( uint32_t ) dpData [ 0 ] < < 24 | ( uint32_t ) dpData [ 1 ] < < 16 | ( uint32_t ) dpData [ 2 ] < < 8 | ( uint32_t ) dpData [ 3 ] < < 0 ;
2020-02-19 07:10:07 +00:00
ResponseAppend_P ( PSTR ( " %u " ) , dpValue ) ;
2020-09-16 19:45:56 +01:00
DataVal = dpValue ;
2020-02-04 10:30:30 +00:00
} else if ( TUYA_TYPE_STRING = = dpDataType ) {
ResponseAppend_P ( PSTR ( " \" %.*s \" " ) , dpDataLen , dpData ) ;
2020-09-16 19:45:56 +01:00
snprintf_P ( DataStr , sizeof ( DataStr ) , PSTR ( " %.*s " ) , dpDataLen , dpData ) ;
2020-02-04 10:30:30 +00:00
} else if ( TUYA_TYPE_ENUM = = dpDataType & & dpDataLen = = 1 ) {
2020-02-19 07:10:07 +00:00
ResponseAppend_P ( PSTR ( " %u " ) , dpData [ 0 ] ) ;
2020-09-16 19:45:56 +01:00
DataVal = dpData [ 0 ] ;
2020-02-04 10:30:30 +00:00
} else {
ResponseAppend_P ( PSTR ( " \" 0x%s \" " ) , dpHexData ) ;
2020-09-16 19:45:56 +01:00
snprintf_P ( DataStr , sizeof ( DataStr ) , PSTR ( " %s " ) , dpHexData ) ;
2020-02-04 10:30:30 +00:00
}
2020-02-03 10:41:46 +00:00
}
ResponseAppend_P ( PSTR ( " , \" %d \" :{ \" DpId \" :%d, \" DpIdType \" :%d, \" DpIdData \" : \" %s \" " ) , dpId , dpId , dpDataType , dpHexData ) ;
if ( TUYA_TYPE_STRING = = dpDataType ) {
ResponseAppend_P ( PSTR ( " , \" Type3Data \" : \" %.*s \" " ) , dpDataLen , dpData ) ;
2020-01-16 15:19:57 +00:00
}
ResponseAppend_P ( PSTR ( " } " ) ) ;
dpidStart + = dpDataLen + 4 ;
2019-10-24 16:03:07 +01:00
}
}
}
ResponseAppend_P ( PSTR ( " }} " ) ) ;
2021-06-11 17:14:12 +01:00
if ( Settings - > flag3 . tuya_serial_mqtt_publish ) { // SetOption66 - Enable TuyaMcuReceived messages over Mqtt
2022-04-01 16:33:57 +01:00
for ( uint8_t cmdsID = 0 ; sizeof ( TuyaExcludeCMDsFromMQTT ) > cmdsID ; cmdsID + + ) {
if ( TuyaExcludeCMDsFromMQTT [ cmdsID ] = = Tuya . buffer [ 3 ] ) {
isCmdToSuppress = true ;
break ;
2022-08-20 12:46:31 +01:00
}
2022-04-01 16:33:57 +01:00
}
if ( ! ( isCmdToSuppress & & Settings - > flag5 . tuya_exclude_from_mqtt ) ) { // SetOption137 - (Tuya) When Set, avoid the (MQTT-) publish of defined Tuya CMDs (see TuyaExcludeCMDsFromMQTT) if SetOption66 is active
2022-03-30 10:21:20 +01:00
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_TUYA_MCU_RECEIVED ) ) ;
2022-04-01 17:21:36 +01:00
} else {
AddLog ( LOG_LEVEL_DEBUG , ResponseData ( ) ) ;
2022-08-20 12:46:31 +01:00
}
2019-10-25 07:05:12 +01:00
} else {
2021-08-15 16:08:31 +01:00
AddLog ( LOG_LEVEL_DEBUG , ResponseData ( ) ) ;
2019-10-25 07:05:12 +01:00
}
2021-04-04 11:04:36 +01:00
XdrvRulesProcess ( 0 ) ;
2018-10-18 12:01:31 +01:00
2021-06-11 17:14:12 +01:00
if ( dpId ! = 0 & & Settings - > tuyamcu_topic ) { // Publish a /STAT Topic ready to use for any home automation system
2020-09-16 19:45:56 +01:00
if ( ! Tuya . SuspendTopic ) {
2020-11-07 21:08:07 +00:00
char scommand [ 13 ] ;
snprintf_P ( scommand , sizeof ( scommand ) , PSTR ( " DpType%uId%u " ) , dpDataType , dpId ) ;
if ( dpDataType ! = 3 & & dpDataType ! = 5 ) { Response_P ( PSTR ( " %u " ) , DataVal ) ; }
else { Response_P ( PSTR ( " %s " ) , DataStr ) ; }
2021-04-20 09:03:48 +01:00
MqttPublishPrefixTopic_P ( STAT , scommand ) ;
2020-09-16 19:45:56 +01:00
}
}
2019-10-14 14:20:47 +01:00
if ( ! Tuya . low_power_mode ) {
TuyaNormalPowerModePacketProcess ( ) ;
} else {
TuyaLowPowerModePacketProcess ( ) ;
}
2019-08-17 14:00:57 +01:00
Tuya . byte_counter = 0 ;
Tuya . cmd_status = 0 ;
Tuya . cmd_checksum = 0 ;
Tuya . data_len = 0 ;
2020-09-16 19:45:56 +01:00
} // read additional packets from TUYA
2019-08-17 14:00:57 +01:00
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 ;
2018-10-18 12:01:31 +01:00
} else {
2019-08-17 14:00:57 +01:00
Tuya . byte_counter = 0 ;
Tuya . cmd_status = 0 ;
Tuya . cmd_checksum = 0 ;
Tuya . data_len = 0 ;
2018-10-18 12:01:31 +01:00
}
}
}
2019-01-28 13:08:33 +00:00
bool TuyaButtonPressed ( void )
2018-10-18 12:01:31 +01:00
{
2019-08-17 15:19:58 +01:00
if ( ! XdrvMailbox . index & & ( ( PRESSED = = XdrvMailbox . payload ) & & ( NOT_PRESSED = = Button . last_state [ XdrvMailbox . index ] ) ) ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Reset GPIO triggered " ) ) ;
2018-11-04 05:56:36 +00:00
TuyaResetWifi ( ) ;
return true ; // Reset GPIO served here
2018-10-27 10:37:42 +01:00
}
2018-11-04 05:56:36 +00:00
return false ; // Don't serve other buttons
2018-10-18 12:01:31 +01:00
}
2019-10-14 14:20:47 +01:00
uint8_t TuyaGetTuyaWifiState ( void ) {
2019-07-02 14:49:08 +01:00
uint8_t wifi_state = 0x02 ;
switch ( WifiState ( ) ) {
case WIFI_MANAGER :
wifi_state = 0x01 ;
break ;
case WIFI_RESTART :
wifi_state = 0x03 ;
break ;
}
2018-10-29 22:35:00 +00:00
2019-10-14 14:20:47 +01:00
if ( MqttIsConnected ( ) ) {
wifi_state = 0x04 ;
}
2018-10-29 00:10:57 +00:00
2019-10-14 14:20:47 +01:00
return wifi_state ;
}
void TuyaSetWifiLed ( void )
{
Tuya . wifi_state = TuyaGetTuyaWifiState ( ) ;
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( " TYA: Set WiFi LED %d (%d) " ) , Tuya . wifi_state , WifiState ( ) ) ;
2019-10-14 14:20:47 +01:00
if ( Tuya . low_power_mode ) {
TuyaSendCmd ( TUYA_LOW_POWER_CMD_WIFI_STATE , & Tuya . wifi_state , 1 ) ;
} else {
TuyaSendCmd ( TUYA_CMD_WIFI_STATE , & Tuya . wifi_state , 1 ) ;
}
2018-10-29 00:10:57 +00:00
}
2020-04-22 10:07:45 +01:00
# ifdef USE_TUYA_TIME
2020-07-16 16:28:47 +01:00
void TuyaSetTime ( void ) {
if ( ! RtcTime . valid ) { return ; }
2020-04-22 10:07:45 +01:00
uint16_t payload_len = 8 ;
uint8_t payload_buffer [ 8 ] ;
2020-12-15 10:20:22 +00:00
uint8_t tuya_day_of_week ;
if ( RtcTime . day_of_week = = 1 ) {
tuya_day_of_week = 7 ;
} else {
tuya_day_of_week = RtcTime . day_of_week - 1 ;
}
2020-04-22 10:07:45 +01:00
payload_buffer [ 0 ] = 0x01 ;
2020-07-25 13:52:37 +01:00
payload_buffer [ 1 ] = RtcTime . year % 100 ;
2020-04-22 10:07:45 +01:00
payload_buffer [ 2 ] = RtcTime . month ;
payload_buffer [ 3 ] = RtcTime . day_of_month ;
payload_buffer [ 4 ] = RtcTime . hour ;
payload_buffer [ 5 ] = RtcTime . minute ;
payload_buffer [ 6 ] = RtcTime . second ;
2020-12-15 10:20:22 +00:00
payload_buffer [ 7 ] = tuya_day_of_week ; //1 for Monday in TUYA Doc
2020-04-22 10:07:45 +01:00
TuyaSendCmd ( TUYA_CMD_SET_TIME , payload_buffer , payload_len ) ;
}
# endif //USE_TUYA_TIME
2020-11-28 14:49:04 +00:00
/*********************************************************************************************\
* Sensors
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void TuyaSensorsShow ( bool json )
{
bool RootName = false ;
bool added = false ;
char sname [ 20 ] ;
char tempval [ 5 ] ;
uint8_t res ;
for ( uint8_t sensor = TUYA_MCU_FUNC_TEMP ; sensor < = TUYA_MCU_FUNC_TIMER4 ; sensor + + ) { // Sensors start from fnId 71
if ( json ) {
if ( TuyaGetDpId ( sensor ) ! = 0 ) {
if ( ! RootName ) {
ResponseAppend_P ( PSTR ( " , \" TuyaSNS \" :{ " ) ) ;
RootName = true ;
}
if ( added ) {
ResponseAppend_P ( PSTR ( " , " ) ) ;
}
if ( sensor > 74 ) {
res = 0 ;
2021-04-26 16:41:19 +01:00
} else if ( sensor > 72 ) {
2021-06-11 17:14:12 +01:00
res = Settings - > flag2 . humidity_resolution ;
2021-04-19 11:25:53 +01:00
} else if ( sensor = = 72 ) {
2021-06-11 17:14:12 +01:00
res = Settings - > mbflag2 . temperature_set_res ;
2021-04-19 11:25:53 +01:00
} else {
2021-06-11 17:14:12 +01:00
res = Settings - > flag2 . temperature_resolution ;
2021-04-19 11:25:53 +01:00
}
2020-11-28 14:49:04 +00:00
GetTextIndexed ( sname , sizeof ( sname ) , ( sensor - 71 ) , kTuyaSensors ) ;
ResponseAppend_P ( PSTR ( " \" %s \" :%s " ) , sname ,
2021-02-18 15:34:33 +00:00
( Tuya . SensorsValid [ sensor - 71 ] ? dtostrfd ( TuyaAdjustedTemperature ( Tuya . Sensors [ sensor - 71 ] , res ) , res , tempval ) : PSTR ( " null " ) ) ) ;
2020-11-28 14:49:04 +00:00
added = true ;
}
# ifdef USE_WEBSERVER
} else {
if ( TuyaGetDpId ( sensor ) ! = 0 ) {
switch ( sensor ) {
case 71 :
2021-06-11 17:14:12 +01:00
WSContentSend_Temp ( " " , TuyaAdjustedTemperature ( Tuya . Sensors [ 0 ] , Settings - > flag2 . temperature_resolution ) ) ;
2020-11-28 14:49:04 +00:00
break ;
case 72 :
WSContentSend_PD ( PSTR ( " {s} " D_TEMPERATURE " Set{m}%s " D_UNIT_DEGREE " %c{e} " ) ,
2021-06-11 17:14:12 +01:00
dtostrfd ( TuyaAdjustedTemperature ( Tuya . Sensors [ 1 ] , Settings - > mbflag2 . temperature_set_res ) , Settings - > mbflag2 . temperature_set_res , tempval ) , TempUnit ( ) ) ;
2020-11-28 14:49:04 +00:00
break ;
case 73 :
2021-06-11 17:14:12 +01:00
WSContentSend_PD ( HTTP_SNS_HUM , " " , dtostrfd ( TuyaAdjustedTemperature ( Tuya . Sensors [ 2 ] , Settings - > flag2 . humidity_resolution ) , Settings - > flag2 . humidity_resolution , tempval ) ) ;
2020-11-28 14:49:04 +00:00
break ;
case 74 :
WSContentSend_PD ( PSTR ( " {s} " D_HUMIDITY " Set{m}%s " D_UNIT_PERCENT " {e} " ) ,
2021-06-11 17:14:12 +01:00
dtostrfd ( TuyaAdjustedTemperature ( Tuya . Sensors [ 3 ] , Settings - > flag2 . humidity_resolution ) , Settings - > flag2 . humidity_resolution , tempval ) ) ;
2020-11-28 14:49:04 +00:00
break ;
case 75 :
WSContentSend_PD ( HTTP_SNS_ILLUMINANCE , " " , Tuya . Sensors [ 4 ] ) ;
break ;
case 76 :
WSContentSend_PD ( PSTR ( " {s} " D_TVOC " {m}%d " D_UNIT_PARTS_PER_MILLION " {e} " ) , Tuya . Sensors [ 5 ] ) ;
break ;
case 77 :
WSContentSend_PD ( HTTP_SNS_CO2 , " " , Tuya . Sensors [ 6 ] ) ;
break ;
case 78 :
WSContentSend_PD ( HTTP_SNS_CO2EAVG , " " , Tuya . Sensors [ 7 ] ) ;
break ;
2021-03-29 19:32:59 +01:00
case 79 :
WSContentSend_PD ( HTTP_SNS_GAS , " " , Tuya . Sensors [ 8 ] ) ;
break ;
2022-08-14 23:58:45 +01:00
case 80 :
WSContentSend_PD ( PSTR ( " {s} " D_ENVIRONMENTAL_CONCENTRATION " 2.5 " D_UNIT_MICROMETER " {m}%d " D_UNIT_MICROGRAM_PER_CUBIC_METER " {e} " ) , Tuya . Sensors [ 9 ] ) ;
break ;
2020-11-28 14:49:04 +00:00
case 81 :
case 82 :
case 83 :
case 84 :
WSContentSend_PD ( PSTR ( " {s}Timer%d{m}%d{e} " ) , ( sensor - 80 ) , Tuya . Sensors [ sensor - 71 ] ) ; // No UoM for timers since they can be sec or min
break ;
}
}
2021-02-02 14:48:29 +00:00
2020-11-28 14:49:04 +00:00
# endif // USE_WEBSERVER
}
}
2021-12-15 11:30:48 +00:00
# ifdef USE_WEBSERVER
2021-02-10 10:15:23 +00:00
if ( AsModuleTuyaMS ( ) ) {
WSContentSend_P ( PSTR ( " {s} " D_JSON_IRHVAC_MODE " {m}%d{e} " ) , Tuya . ModeSet ) ;
}
2021-12-15 11:30:48 +00:00
# endif // USE_WEBSERVER
2021-02-10 10:15:23 +00:00
2020-11-28 14:49:04 +00:00
if ( RootName ) { ResponseJsonEnd ( ) ; }
}
2021-12-15 11:30:48 +00:00
# ifdef USE_WEBSERVER
void TuyaAddButton ( void ) {
if ( AsModuleTuyaMS ( ) ) {
WSContentSend_P ( HTTP_TABLE100 ) ;
WSContentSend_P ( PSTR ( " <tr><div></div> " ) ) ;
char stemp [ 33 ] ;
snprintf_P ( stemp , sizeof ( stemp ) , PSTR ( " " D_JSON_IRHVAC_MODE " " ) ) ;
WSContentSend_P ( HTTP_DEVICE_CONTROL , 26 , TasmotaGlobal . devices_present + 1 ,
( strlen ( SettingsText ( SET_BUTTON1 + TasmotaGlobal . devices_present ) ) ) ? SettingsText ( SET_BUTTON1 + TasmotaGlobal . devices_present ) : stemp , " " ) ;
WSContentSend_P ( PSTR ( " </tr></table> " ) ) ;
}
}
# endif // USE_WEBSERVER
2018-10-18 12:01:31 +01:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-12-19 15:41:10 +00:00
# ifdef USE_ENERGY_SENSOR
bool Xnrg32 ( uint8_t function )
2018-10-18 12:01:31 +01:00
{
2019-01-28 13:08:33 +00:00
bool result = false ;
2018-10-18 12:01:31 +01:00
2021-12-19 15:41:10 +00:00
if ( Tuya . active ) {
if ( FUNC_PRE_INIT = = function ) {
if ( TuyaGetDpId ( TUYA_MCU_FUNC_POWER ) ! = 0 | | TuyaGetDpId ( TUYA_MCU_FUNC_POWER_COMBINED ) ! = 0 ) {
if ( TuyaGetDpId ( TUYA_MCU_FUNC_CURRENT ) = = 0 & & TuyaGetDpId ( TUYA_MCU_FUNC_POWER_COMBINED ) = = 0 ) {
Energy . current_available = false ;
}
if ( TuyaGetDpId ( TUYA_MCU_FUNC_VOLTAGE ) = = 0 & & TuyaGetDpId ( TUYA_MCU_FUNC_POWER_COMBINED ) = = 0 ) {
Energy . voltage_available = false ;
}
TasmotaGlobal . energy_driver = XNRG_32 ;
}
}
}
return result ;
}
# endif // USE_ENERGY_SENSOR
bool Xdrv16 ( uint8_t function ) {
bool result = false ;
if ( FUNC_MODULE_INIT = = function ) {
result = TuyaModuleSelected ( ) ;
Tuya . active = result ;
}
else if ( Tuya . active ) {
2018-10-18 12:01:31 +01:00
switch ( function ) {
2019-03-30 12:03:45 +00:00
case FUNC_LOOP :
if ( TuyaSerial ) { TuyaSerialInput ( ) ; }
break ;
2020-01-11 14:39:56 +00:00
case FUNC_PRE_INIT :
2018-10-18 12:01:31 +01:00
TuyaInit ( ) ;
break ;
2018-10-19 11:53:22 +01:00
case FUNC_SET_DEVICE_POWER :
2018-10-18 12:01:31 +01:00
result = TuyaSetPower ( ) ;
break ;
2018-10-19 11:53:22 +01:00
case FUNC_BUTTON_PRESSED :
result = TuyaButtonPressed ( ) ;
break ;
2018-10-29 00:10:57 +00:00
case FUNC_EVERY_SECOND :
2019-10-14 14:20:47 +01:00
if ( TuyaSerial & & Tuya . wifi_state ! = TuyaGetTuyaWifiState ( ) ) { TuyaSetWifiLed ( ) ; }
if ( ! Tuya . low_power_mode ) {
Tuya . heartbeat_timer + + ;
if ( Tuya . heartbeat_timer > 10 ) {
Tuya . heartbeat_timer = 0 ;
TuyaSendCmd ( TUYA_CMD_HEARTBEAT ) ;
}
2020-07-16 16:28:47 +01:00
# ifdef USE_TUYA_TIME
2020-10-28 16:32:07 +00:00
if ( ! ( TasmotaGlobal . uptime % 60 ) ) {
2020-07-16 15:42:06 +01:00
TuyaSetTime ( ) ;
2020-07-16 16:13:19 +01:00
}
2020-07-16 16:28:47 +01:00
# endif //USE_TUYA_TIME
2019-10-14 14:20:47 +01:00
} else {
2020-07-16 16:28:47 +01:00
TuyaSendLowPowerSuccessIfNeeded ( ) ;
2019-05-19 00:13:57 +01:00
}
2020-09-16 19:45:56 +01:00
if ( Tuya . ignore_topic_timeout < millis ( ) ) { Tuya . SuspendTopic = false ; }
2018-10-29 00:27:45 +00:00
break ;
2020-11-28 14:49:04 +00:00
case FUNC_SET_CHANNELS :
result = TuyaSetChannels ( ) ;
break ;
case FUNC_MQTT_INIT :
TuyaSendCmd ( TUYA_CMD_QUERY_PRODUCT ) ;
break ;
2019-09-03 11:23:44 +01:00
case FUNC_COMMAND :
result = DecodeCommand ( kTuyaCommand , TuyaCommand ) ;
break ;
2020-11-28 14:49:04 +00:00
case FUNC_JSON_APPEND :
TuyaSensorsShow ( 1 ) ;
break ;
# ifdef USE_WEBSERVER
2021-12-15 11:30:48 +00:00
case FUNC_WEB_ADD_MAIN_BUTTON :
TuyaAddButton ( ) ;
break ;
2020-11-28 14:49:04 +00:00
case FUNC_WEB_SENSOR :
TuyaSensorsShow ( 0 ) ;
break ;
# endif // USE_WEBSERVER
2018-10-18 12:01:31 +01:00
}
}
return result ;
}
2019-09-03 12:23:05 +01:00
# endif // USE_TUYA_MCU
2019-06-16 15:43:23 +01:00
# endif // USE_LIGHT