2019-09-29 14:38:26 +01:00
/*
2019-10-27 10:13:24 +00:00
xdrv_23_zigbee . ino - zigbee support for Tasmota
2019-09-29 14:38:26 +01:00
2021-01-01 12:44:04 +00:00
Copyright ( C ) 2021 Theo Arends and Stephan Hadinger
2019-09-29 14:38:26 +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/>.
*/
# ifdef USE_ZIGBEE
2020-06-19 19:54:37 +01:00
# ifdef USE_ZIGBEE_EZSP
2020-11-30 18:12:16 +00:00
void EZ_SendZDO ( uint16_t shortaddr , uint16_t cmd , const unsigned char * payload , size_t payload_len , bool retry = true ) ;
2020-07-29 09:02:04 +01:00
//
// Trying to get a uniform LQI measure, we are aligning with the definition of ZNP
// I.e. a linear projection from -87dBm to +10dB over 0..255
// for ZNP, lqi is linear from -87 to +10 dBm (https://sunmaysky.blogspot.com/2017/02/conversion-between-rssi-and-lqi-in-z.html)
uint8_t ZNP_RSSI2Lqi ( int8_t rssi ) {
if ( rssi < - 87 ) { rssi = - 87 ; }
if ( rssi > 10 ) { rssi = 10 ; }
2020-10-12 18:35:50 +01:00
return changeUIntScale ( rssi + 87 , 0 , 87 + 10 , 0 , 254 ) ;
2020-07-29 09:02:04 +01:00
}
2020-06-19 19:54:37 +01:00
/*********************************************************************************************\
* Parsers for incoming EZSP messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-07-02 21:56:37 +01:00
// EZSP: received ASH "RSTACK" frame, indicating that the MCU finished boot
2020-11-12 18:38:21 +00:00
void EZ_RSTACK ( uint8_t reset_code ) {
2020-06-19 19:54:37 +01:00
const char * reason_str ;
switch ( reset_code ) {
case 0x01 : reason_str = PSTR ( " External " ) ; break ;
case 0x02 : reason_str = PSTR ( " Power-on " ) ; break ;
case 0x03 : reason_str = PSTR ( " Watchdog " ) ; break ;
case 0x06 : reason_str = PSTR ( " Assert " ) ; break ;
case 0x09 : reason_str = PSTR ( " Bootloader " ) ; break ;
case 0x0B : reason_str = PSTR ( " Software " ) ; break ;
case 0x00 :
default : reason_str = PSTR ( " Unknown " ) ; break ;
}
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2022-02-15 11:11:45 +00:00
" \" Status \" :%d, \" Message \" : \" EFR32 EZSP booted \" , \" RestartReason \" : \" %s \" "
2020-06-19 19:54:37 +01:00
" , \" Code \" :%d}} " ) ,
ZIGBEE_STATUS_BOOT , reason_str , reset_code ) ;
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
2020-06-19 19:54:37 +01:00
}
2020-07-02 21:56:37 +01:00
// EZSP: received ASH "ERROR" frame, indicating that the MCU finished boot
2020-11-12 18:38:21 +00:00
void EZ_ERROR ( uint8_t error_code ) {
2020-06-19 19:54:37 +01:00
const char * reason_str ;
switch ( error_code ) {
case 0x51 : reason_str = PSTR ( " ACK timeout " ) ; break ;
default : reason_str = PSTR ( " Unknown " ) ; break ;
}
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
" \" Status \" :%d, \" Message \" : \" Failed state \" , \" Error \" : \" %s \" "
" , \" Code \" :%d}} " ) ,
ZIGBEE_STATUS_ABORT , reason_str , error_code ) ;
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
2020-06-19 19:54:37 +01:00
}
2021-01-25 15:02:56 +00:00
int32_t EZ_ReadAPSUnicastMessage ( int32_t res , SBuffer & buf ) {
2020-06-19 19:54:37 +01:00
// Called when receiving a response from getConfigurationValue
// Value is in bytes 2+3
2020-11-12 18:38:21 +00:00
// uint16_t value = buf.get16(2);
2020-06-19 19:54:37 +01:00
return res ;
}
2020-07-02 21:56:37 +01:00
/*********************************************************************************************\
* Parsers for incoming EZSP messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//
// Handle a "getEui64" incoming message
//
2021-01-25 15:02:56 +00:00
int32_t EZ_GetEUI64 ( int32_t res , SBuffer & buf ) {
2020-07-02 21:56:37 +01:00
localIEEEAddr = buf . get64 ( 2 ) ;
return res ;
}
//
// Handle a "getEui64" incoming message
//
2021-01-25 15:02:56 +00:00
int32_t EZ_GetNodeId ( int32_t res , SBuffer & buf ) {
2020-07-02 21:56:37 +01:00
localShortAddr = buf . get8 ( 2 ) ;
return res ;
}
//
// Handle a "getNetworkParameters" incoming message
//
2021-01-25 15:02:56 +00:00
int32_t EZ_NetworkParameters ( int32_t res , SBuffer & buf ) {
2020-07-02 21:56:37 +01:00
uint8_t node_type = buf . get8 ( 3 ) ;
// ext panid: 4->11
// panid: 12->13
// radioTxPower: 14
// radioChannel: 15
// Local short and long addresses are supposed to be already retrieved
// localIEEEAddr = long_adr;
// localShortAddr = short_adr;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2021-01-24 15:35:36 +00:00
" \" Status \" :%d, \" IEEEAddr \" : \" 0x%_X \" , \" ShortAddr \" : \" 0x%04X \" "
2020-07-02 21:56:37 +01:00
" , \" DeviceType \" :%d}} " ) ,
2021-01-24 15:35:36 +00:00
ZIGBEE_STATUS_EZ_INFO , & localIEEEAddr , localShortAddr , node_type ) ;
2020-07-02 21:56:37 +01:00
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
2020-07-02 21:56:37 +01:00
return res ;
}
2020-06-19 19:54:37 +01:00
2020-08-05 19:49:07 +01:00
//
// Analyze response to "getKey" and check NWK key
//
2021-01-25 15:02:56 +00:00
int32_t EZ_CheckKeyNWK ( int32_t res , SBuffer & buf ) {
2020-11-12 18:38:21 +00:00
// uint8_t status = buf.get8(2);
// uint16_t bitmask = buf.get16(3);
2020-08-05 19:49:07 +01:00
uint8_t key_type = buf . get8 ( 5 ) ;
uint64_t key_low = buf . get64 ( 6 ) ;
uint64_t key_high = buf . get64 ( 14 ) ;
if ( ( key_type = = EMBER_CURRENT_NETWORK_KEY ) & &
( key_low = = ezsp_key_low ) & &
( key_high = = ezsp_key_high ) ) {
return 0 ; // proceed to next step
} else {
return - 2 ; // error state
}
}
2020-07-25 14:40:42 +01:00
//
// Handle a "incomingRouteErrorHandler" incoming message
//
2021-01-25 15:02:56 +00:00
int32_t EZ_RouteError ( int32_t res , const SBuffer & buf ) {
2020-07-25 14:40:42 +01:00
uint8_t status = buf . get8 ( 2 ) ;
uint16_t shortaddr = buf . get16 ( 3 ) ;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_ROUTE_ERROR " \" :{ "
2020-07-29 09:02:04 +01:00
" \" ShortAddr \" : \" 0x%04X \" , \" " D_JSON_ZIGBEE_STATUS " \" :%d, \" " D_JSON_ZIGBEE_STATUS_MSG " \" : \" %s \" }} " ) ,
shortaddr , status , getEmberStatus ( status ) . c_str ( ) ) ;
2020-07-25 14:40:42 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
return - 1 ;
}
2021-02-03 19:37:44 +00:00
//
// Handle EZSP Energy Scan result
//
int32_t EZSP_EnergyScanResult ( int32_t res , const SBuffer & buf ) {
uint8_t channel = buf . get8 ( 2 ) ;
int8_t energy = buf . get8 ( 3 ) ;
if ( ( channel > = USE_ZIGBEE_CHANNEL_MIN ) & & ( channel < = USE_ZIGBEE_CHANNEL_MAX ) ) {
zigbee . energy [ channel - USE_ZIGBEE_CHANNEL_MIN ] = energy ;
}
return - 1 ;
}
//
// Handle EZSP Energy Scan complete
//
int32_t EZSP_EnergyScanComplete ( int32_t res , const SBuffer & buf ) {
// uint8_t channel = buf.get8(2);
uint8_t status = buf . get8 ( 3 ) ;
if ( 0 = = status ) {
EnergyScanResults ( ) ;
}
return - 1 ;
}
//
// Dump energu scan results
//
void EnergyScanResults ( void ) {
2021-03-09 22:05:12 +00:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_SCAN " \" :{ " ) ) ;
2021-02-03 19:37:44 +00:00
for ( uint32_t i = 0 ; i < USE_ZIGBEE_CHANNEL_COUNT ; i + + ) {
int8_t energy = zigbee . energy [ i ] ;
if ( i > 0 ) { ResponseAppend_P ( PSTR ( " , " ) ) ; }
ResponseAppend_P ( PSTR ( " \" %d \" :%d " ) , i + USE_ZIGBEE_CHANNEL_MIN , energy ) ;
// display as log
static const int32_t bar_min = - 87 ;
static const int32_t bar_max = 10 ;
static const uint32_t bar_count = 60 ;
char bar_str [ bar_count + 1 ] ;
uint32_t energy_unsigned = energy + 0x80 ;
uint32_t bars = changeUIntScale ( energy_unsigned , bar_min + 0x80 , bar_max + 0x80 , 0 , bar_count ) ;
for ( uint32_t j = 0 ; j < bars ; j + + ) { bar_str [ j ] = ' # ' ; }
bar_str [ bars ] = 0 ;
2021-02-28 11:50:02 +00:00
2021-02-03 19:37:44 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_ZIGBEE " Channel %2d: %s " ) , i + USE_ZIGBEE_CHANNEL_MIN , bar_str ) ;
}
2021-03-09 22:05:12 +00:00
ResponseAppend_P ( PSTR ( " }} " ) ) ;
2021-02-03 19:37:44 +00:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
}
2020-07-25 14:40:42 +01:00
//
// Handle a "permitJoining" incoming message
//
2021-01-25 15:02:56 +00:00
int32_t EZ_PermitJoinRsp ( int32_t res , const SBuffer & buf ) {
2020-07-25 14:40:42 +01:00
uint8_t status = buf . get8 ( 2 ) ;
2020-10-30 11:29:48 +00:00
2020-09-24 18:15:07 +01:00
if ( status ) { // only report if there is an error
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ \" Status \" :23, \" Message \" : \" Pairing mode error 0x%02X \" }} " ) , status ) ;
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
2020-07-25 14:40:42 +01:00
}
return - 1 ;
}
2020-08-28 21:53:34 +01:00
//
// Received MessageSentHandler
//
// We normally ignore the message, but it's a way to sniff group ids for IKEA remote
// In case of a multicast message sent to 0xFFFD with non-null group id, we log the group id
2021-01-25 15:02:56 +00:00
int32_t EZ_MessageSent ( int32_t res , const SBuffer & buf ) {
2020-08-28 21:53:34 +01:00
uint8_t message_type = buf . get8 ( 2 ) ;
uint16_t dst_addr = buf . get16 ( 3 ) ;
uint16_t group_addr = buf . get16 ( 13 ) ;
if ( ( EMBER_OUTGOING_MULTICAST = = message_type ) & & ( 0xFFFD = = dst_addr ) ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_ZIGBEE " Sniffing group 0x%04X " ) , group_addr ) ;
2020-08-28 21:53:34 +01:00
}
return - 1 ; // ignore
}
2020-06-19 19:54:37 +01:00
# endif // USE_ZIGBEE_EZSP
2021-02-18 19:04:41 +00:00
//
// Special case: EZSP does not send an event for PermitJoin end, so we generate a synthetic one
//
void Z_PermitJoinDisable ( void ) {
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ \" Status \" :20, \" Message \" : \" Pairing mode disabled \" }} " ) ) ;
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
}
2020-11-30 18:12:16 +00:00
/*********************************************************************************************\
* Handle auto - mapping
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// low-level sending of packet
void Z_Send_State_or_Map ( uint16_t shortaddr , uint8_t index , uint16_t zdo_cmd ) {
# ifdef USE_ZIGBEE_ZNP
SBuffer buf ( 10 ) ;
buf . add8 ( Z_SREQ | Z_ZDO ) ; // 25
buf . add8 ( zdo_cmd ) ; // 33
buf . add16 ( shortaddr ) ; // shortaddr
buf . add8 ( index ) ; // StartIndex = 0
ZigbeeZNPSend ( buf . getBuffer ( ) , buf . len ( ) ) ;
# endif // USE_ZIGBEE_ZNP
# ifdef USE_ZIGBEE_EZSP
// ZDO message payload (see Zigbee spec 2.4.3.3.4)
uint8_t buf [ ] = { index } ; // index = 0
EZ_SendZDO ( shortaddr , zdo_cmd , buf , sizeof ( buf ) , false ) ;
# endif // USE_ZIGBEE_EZSP
}
// This callback is registered to send ZbMap(s) to each device one at a time
void Z_Map ( uint16_t shortaddr , uint16_t groupaddr , uint16_t cluster , uint8_t endpoint , uint32_t value ) {
if ( BAD_SHORTADDR ! = shortaddr ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_ZIGBEE " sending `ZbMap 0x%04X` " ) , shortaddr ) ;
2020-11-30 18:12:16 +00:00
# ifdef USE_ZIGBEE_ZNP
Z_Send_State_or_Map ( shortaddr , value , ZDO_MGMT_LQI_REQ ) ;
# endif // USE_ZIGBEE_ZNP
# ifdef USE_ZIGBEE_EZSP
Z_Send_State_or_Map ( shortaddr , value , ZDO_Mgmt_Lqi_req ) ;
# endif // USE_ZIGBEE_EZSP
} else {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_ZIGBEE " ZbMap done " ) ) ;
2020-12-12 18:05:47 +00:00
zigbee . mapping_in_progress = false ;
zigbee . mapping_ready = true ;
2020-11-30 18:12:16 +00:00
}
}
2020-06-29 21:21:32 +01:00
/*********************************************************************************************\
* Parsers for incoming EZSP messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//
// Handle a "getEui64" incoming message
//
2021-01-25 15:02:56 +00:00
int32_t Z_EZSPGetEUI64 ( int32_t res , SBuffer & buf ) {
2020-06-29 21:21:32 +01:00
localIEEEAddr = buf . get64 ( 2 ) ;
return res ;
}
//
// Handle a "getEui64" incoming message
//
2021-01-25 15:02:56 +00:00
int32_t Z_EZSPGetNodeId ( int32_t res , SBuffer & buf ) {
2020-06-29 21:21:32 +01:00
localShortAddr = buf . get8 ( 2 ) ;
return res ;
}
//
// Handle a "getNetworkParameters" incoming message
//
2021-01-25 15:02:56 +00:00
int32_t Z_EZSPNetworkParameters ( int32_t res , SBuffer & buf ) {
2020-06-29 21:21:32 +01:00
uint8_t node_type = buf . get8 ( 3 ) ;
// ext panid: 4->11
// panid: 12->13
// radioTxPower: 14
// radioChannel: 15
// Local short and long addresses are supposed to be already retrieved
// localIEEEAddr = long_adr;
// localShortAddr = short_adr;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2021-01-24 15:35:36 +00:00
" \" Status \" :%d, \" IEEEAddr \" : \" 0x%_X \" , \" ShortAddr \" : \" 0x%04X \" "
2020-06-29 21:21:32 +01:00
" , \" DeviceType \" :%d}} " ) ,
2021-01-24 15:35:36 +00:00
ZIGBEE_STATUS_EZ_INFO , & localIEEEAddr , localShortAddr , node_type ) ;
2020-06-29 21:21:32 +01:00
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
2020-06-29 21:21:32 +01:00
return res ;
}
2020-03-23 21:46:26 +00:00
/*********************************************************************************************\
* Parsers for incoming ZNP messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//
// Handle a "Receive Device Info" incoming message
//
2021-01-25 15:02:56 +00:00
int32_t ZNP_ReceiveDeviceInfo ( int32_t res , SBuffer & buf ) {
2019-09-29 14:38:26 +01:00
// Ex= 6700.00.6263151D004B1200.0000.07.09.02.83869991
// IEEE Adr (8 bytes) = 0x00124B001D156362
// Short Addr (2 bytes) = 0x0000
// Device Type (1 byte) = 0x07 (coord?)
// Device State (1 byte) = 0x09 (coordinator started)
// NumAssocDevices (1 byte) = 0x02
// List of devices: 0x8683, 0x9199
Z_IEEEAddress long_adr = buf . get64 ( 3 ) ;
Z_ShortAddress short_adr = buf . get16 ( 11 ) ;
uint8_t device_type = buf . get8 ( 13 ) ;
uint8_t device_state = buf . get8 ( 14 ) ;
uint8_t device_associated = buf . get8 ( 15 ) ;
2020-02-02 19:53:49 +00:00
// keep track of the local IEEE address
localIEEEAddr = long_adr ;
2020-06-29 21:21:32 +01:00
localShortAddr = short_adr ;
2020-02-02 19:53:49 +00:00
2019-11-09 08:25:15 +00:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2021-01-24 15:35:36 +00:00
" \" Status \" :%d, \" IEEEAddr \" : \" 0x%_X \" , \" ShortAddr \" : \" 0x%04X \" "
2019-09-29 14:38:26 +01:00
" , \" DeviceType \" :%d, \" DeviceState \" :%d "
" , \" NumAssocDevices \" :%d " ) ,
2021-01-24 15:35:36 +00:00
ZIGBEE_STATUS_CC_INFO , & long_adr , short_adr , device_type , device_state ,
2019-09-29 14:38:26 +01:00
device_associated ) ;
2020-05-17 17:33:42 +01:00
if ( device_associated > 0 ) { // If there are devices registered in CC2530, print the list
2019-09-29 14:38:26 +01:00
uint idx = 16 ;
ResponseAppend_P ( PSTR ( " , \" AssocDevicesList \" :[ " ) ) ;
for ( uint32_t i = 0 ; i < device_associated ; i + + ) {
if ( i > 0 ) { ResponseAppend_P ( PSTR ( " , " ) ) ; }
ResponseAppend_P ( PSTR ( " \" 0x%04X \" " ) , buf . get16 ( idx ) ) ;
idx + = 2 ;
}
ResponseAppend_P ( PSTR ( " ] " ) ) ;
}
2020-07-20 16:24:51 +01:00
ResponseJsonEndEnd ( ) ; // append '}}'
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
2019-09-29 14:38:26 +01:00
return res ;
}
2021-01-25 15:02:56 +00:00
int32_t ZNP_CheckNVWrite ( int32_t res , SBuffer & buf ) {
2019-09-29 14:38:26 +01:00
// Check the status after NV Init "ZNP Has Configured"
// Good response should be 610700 or 610709 (Success or Created)
// We only filter the response on 6107 and check the code in this function
uint8_t status = buf . get8 ( 2 ) ;
if ( ( 0x00 = = status ) | | ( 0x09 = = status ) ) {
return 0 ; // Ok, continue
} else {
return - 2 ; // Error
}
}
2021-01-25 15:02:56 +00:00
int32_t ZNP_Reboot ( int32_t res , SBuffer & buf ) {
2019-10-13 11:56:52 +01:00
// print information about the reboot of device
// 4180.02.02.00.02.06.03
2019-10-27 10:13:24 +00:00
//
2019-10-13 11:56:52 +01:00
uint8_t reason = buf . get8 ( 2 ) ;
2020-11-12 18:38:21 +00:00
// uint8_t transport_rev = buf.get8(3);
// uint8_t product_id = buf.get8(4);
2019-10-13 11:56:52 +01:00
uint8_t major_rel = buf . get8 ( 5 ) ;
uint8_t minor_rel = buf . get8 ( 6 ) ;
2020-11-12 18:38:21 +00:00
// uint8_t hw_rev = buf.get8(7);
2020-05-17 17:33:42 +01:00
const char * reason_str ;
2019-10-13 11:56:52 +01:00
2020-05-17 17:33:42 +01:00
switch ( reason ) {
case 0 : reason_str = PSTR ( " Power-up " ) ; break ;
case 1 : reason_str = PSTR ( " External " ) ; break ;
case 2 : reason_str = PSTR ( " Watchdog " ) ; break ;
default : reason_str = PSTR ( " Unknown " ) ; break ;
}
2019-10-13 11:56:52 +01:00
2019-11-09 08:25:15 +00:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2022-02-15 11:11:45 +00:00
" \" Status \" :%d, \" Message \" : \" CCxxxx ZNP booted \" , \" RestartReason \" : \" %s \" "
2019-10-13 11:56:52 +01:00
" , \" MajorRel \" :%d, \" MinorRel \" :%d}} " ) ,
2020-03-16 17:55:58 +00:00
ZIGBEE_STATUS_BOOT , reason_str ,
2019-10-13 11:56:52 +01:00
major_rel , minor_rel ) ;
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
2019-10-13 11:56:52 +01:00
2021-01-28 18:23:00 +00:00
if ( ( 0x02 = = major_rel ) & & ( ( 0x06 = = minor_rel ) | | ( 0x07 = = minor_rel ) ) ) {
2021-02-18 19:04:41 +00:00
if ( 0x07 = = minor_rel ) {
zigbee . zb3 = true ; // we run Zigbee 3
ZNP_UpdateZStack3 ( ) ; // update configuration for ZStack 3
}
2021-01-28 18:23:00 +00:00
return 0 ; // version 2.6.x and 2.7.x are ok
2019-10-13 11:56:52 +01:00
} else {
return ZIGBEE_LABEL_UNSUPPORTED_VERSION ; // abort
}
}
2020-06-29 21:21:32 +01:00
# ifdef USE_ZIGBEE_ZNP
2021-01-25 15:02:56 +00:00
int32_t ZNP_ReceiveCheckVersion ( int32_t res , SBuffer & buf ) {
2019-09-29 14:38:26 +01:00
// check that the version is supported
// typical version for ZNP 1.2
// 61020200-02.06.03.D9143401.0200000000
// TranportRev = 02
// Product = 00
// MajorRel = 2
// MinorRel = 6
// MaintRel = 3
// Revision = 20190425 d (0x013414D9)
2022-05-25 18:18:39 +01:00
zigbee . major_rel = buf . get8 ( 4 ) ;
zigbee . minor_rel = buf . get8 ( 5 ) ;
zigbee . maint_rel = buf . get8 ( 6 ) ;
zigbee . revision = buf . get32 ( 7 ) ;
2019-09-29 14:38:26 +01:00
2019-11-09 08:25:15 +00:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2019-09-29 14:38:26 +01:00
" \" Status \" :%d, \" MajorRel \" :%d, \" MinorRel \" :%d "
" , \" MaintRel \" :%d, \" Revision \" :%d}} " ) ,
2022-05-25 18:18:39 +01:00
ZIGBEE_STATUS_CC_VERSION ,
zigbee . major_rel , zigbee . minor_rel ,
zigbee . maint_rel , zigbee . revision ) ;
2019-09-29 14:38:26 +01:00
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
2019-09-29 14:38:26 +01:00
2022-05-25 18:18:39 +01:00
if ( ( 0x02 = = zigbee . major_rel ) & & ( ( 0x06 = = zigbee . minor_rel ) | | ( 0x07 = = zigbee . minor_rel ) ) ) {
2021-01-28 18:23:00 +00:00
return 0 ; // version 2.6.x and 2.7.x are ok
2019-09-29 14:38:26 +01:00
} else {
return ZIGBEE_LABEL_UNSUPPORTED_VERSION ; // abort
}
2020-07-02 21:56:37 +01:00
}
2020-06-29 21:21:32 +01:00
# endif // USE_ZIGBEE_ZNP
# ifdef USE_ZIGBEE_EZSP
2021-01-25 15:02:56 +00:00
int32_t EZ_ReceiveCheckVersion ( int32_t res , SBuffer & buf ) {
2020-06-29 21:21:32 +01:00
uint8_t protocol_version = buf . get8 ( 2 ) ;
uint8_t stack_type = buf . get8 ( 3 ) ;
2021-01-25 21:21:13 +00:00
zigbee . ezsp_version = buf . get16 ( 4 ) ;
2020-06-29 21:21:32 +01:00
2022-05-25 18:18:39 +01:00
zigbee . major_rel = ( zigbee . ezsp_version & 0xF000 ) > > 12 ;
zigbee . minor_rel = ( zigbee . ezsp_version & 0x0F00 ) > > 8 ;
zigbee . maint_rel = ( zigbee . ezsp_version & 0x00F0 ) > > 4 ;
zigbee . revision = zigbee . ezsp_version & 0x000F ;
2020-06-29 21:21:32 +01:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
" \" Status \" :%d, \" Version \" : \" %d.%d.%d.%d \" , \" Protocol \" :%d "
" , \" Stack \" :%d}} " ) ,
2020-07-05 11:35:54 +01:00
ZIGBEE_STATUS_EZ_VERSION ,
2022-05-25 18:18:39 +01:00
zigbee . major_rel , zigbee . minor_rel ,
zigbee . maint_rel , zigbee . revision ,
2020-06-29 21:21:32 +01:00
protocol_version ,
stack_type
) ;
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
2020-06-29 21:21:32 +01:00
if ( 0x08 = = protocol_version ) {
2021-01-25 21:21:13 +00:00
if ( ( zigbee . ezsp_version & 0xFF00 ) = = 0x6700 ) {
2020-09-25 16:51:03 +01:00
// If v6.7 there is a bug so we need to change the response
ZBW ( ZBR_SET_OK2 , 0x00 , 0x00 /*high*/ , 0x00 /*ok*/ )
}
2020-06-29 21:21:32 +01:00
return 0 ; // protocol v8 is ok
} else {
return ZIGBEE_LABEL_UNSUPPORTED_VERSION ; // abort
}
2019-09-29 14:38:26 +01:00
}
2020-10-10 15:19:37 +01:00
bool EZ_reset_config = false ;
2020-07-02 21:56:37 +01:00
// Set or clear reset_config
int32_t EZ_Set_ResetConfig ( uint8_t value ) {
EZ_reset_config = value ? true : false ;
return 0 ;
}
// checks if we need to reset the configuration of the device
// if reset_config == 0, continue
// if reset_config == 1, goto ZIGBEE_LABEL_CONFIGURE_EZSP
int32_t EZ_GotoIfResetConfig ( uint8_t value ) {
if ( EZ_reset_config ) { return ZIGBEE_LABEL_CONFIGURE_EZSP ; }
else { return 0 ; }
}
# endif // USE_ZIGBEE_EZSP
2020-05-11 20:16:17 +01:00
// checks the device type (coordinator, router, end-device)
// If coordinator continue
// If router goto ZIGBEE_LABEL_START_ROUTER
// If device goto ZIGBEE_LABEL_START_DEVICE
2021-01-25 15:02:56 +00:00
int32_t Z_SwitchDeviceType ( int32_t res , SBuffer & buf ) {
2021-06-11 17:14:12 +01:00
switch ( Settings - > zb_pan_id ) {
2020-05-11 20:16:17 +01:00
case 0xFFFF : return ZIGBEE_LABEL_INIT_ROUTER ;
case 0xFFFE : return ZIGBEE_LABEL_INIT_DEVICE ;
default : return 0 ; // continue
}
}
2020-03-23 21:46:26 +00:00
//
// Helper function, checks if the incoming buffer matches the 2-bytes prefix, i.e. message type in PMEM
//
2021-01-25 15:02:56 +00:00
bool Z_ReceiveMatchPrefix ( const SBuffer & buf , const uint8_t * match ) {
2019-09-29 14:38:26 +01:00
if ( ( pgm_read_byte ( & match [ 0 ] ) = = buf . get8 ( 0 ) ) & &
( pgm_read_byte ( & match [ 1 ] ) = = buf . get8 ( 1 ) ) ) {
return true ;
} else {
return false ;
}
}
2020-03-23 21:46:26 +00:00
//
// Handle Permit Join response
//
2021-01-25 15:02:56 +00:00
int32_t ZNP_ReceivePermitJoinStatus ( int32_t res , const SBuffer & buf ) {
2019-09-29 14:38:26 +01:00
// we received a PermitJoin status change
uint8_t duration = buf . get8 ( 2 ) ;
uint8_t status_code ;
const char * message ;
2021-02-18 19:04:41 +00:00
if ( ! zigbee . zb3 & & ( 0xFF = = duration ) ) {
2019-09-29 14:38:26 +01:00
status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_XX ;
message = PSTR ( " Enable Pairing mode until next boot " ) ;
2021-02-18 19:04:41 +00:00
zigbee . permit_end_time = - 1 ; // In ZNP mode, declare permitjoin open
2019-09-29 14:38:26 +01:00
} else if ( duration > 0 ) {
status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_60 ;
message = PSTR ( " Enable Pairing mode for %d seconds " ) ;
2021-02-18 19:04:41 +00:00
zigbee . permit_end_time = - 1 ; // In ZNP mode, declare permitjoin open
2019-09-29 14:38:26 +01:00
} else {
status_code = ZIGBEE_STATUS_PERMITJOIN_CLOSE ;
message = PSTR ( " Disable Pairing mode " ) ;
2021-02-18 19:04:41 +00:00
zigbee . permit_end_time = 0 ; // In ZNP mode, declare permitjoin closed
2019-09-29 14:38:26 +01:00
}
2019-11-09 08:25:15 +00:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2019-09-29 14:38:26 +01:00
" \" Status \" :%d, \" Message \" : \" " ) ,
status_code ) ;
ResponseAppend_P ( message , duration ) ;
ResponseAppend_P ( PSTR ( " \" }} " ) ) ;
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
2019-09-29 14:38:26 +01:00
return - 1 ;
}
2020-07-02 21:56:37 +01:00
//
// ZNP only
//
2021-01-25 15:02:56 +00:00
int32_t ZNP_ReceiveNodeDesc ( int32_t res , const SBuffer & buf ) {
2019-09-29 14:38:26 +01:00
// Received ZDO_NODE_DESC_RSP
2020-11-12 18:38:21 +00:00
// Z_ShortAddress srcAddr = buf.get16(2);
2019-09-29 14:38:26 +01:00
uint8_t status = buf . get8 ( 4 ) ;
2020-11-12 18:38:21 +00:00
// Z_ShortAddress nwkAddr = buf.get16(5);
2019-09-29 14:38:26 +01:00
uint8_t logicalType = buf . get8 ( 7 ) ;
2020-11-12 18:38:21 +00:00
// uint8_t apsFlags = buf.get8(8);
// uint8_t MACCapabilityFlags = buf.get8(9);
// uint16_t manufacturerCapabilities = buf.get16(10);
// uint8_t maxBufferSize = buf.get8(12);
// uint16_t maxInTransferSize = buf.get16(13);
// uint16_t serverMask = buf.get16(15);
// uint16_t maxOutTransferSize = buf.get16(17);
// uint8_t descriptorCapabilities = buf.get8(19);
2019-09-29 14:38:26 +01:00
2020-05-17 17:33:42 +01:00
2019-09-29 14:38:26 +01:00
if ( 0 = = status ) {
uint8_t deviceType = logicalType & 0x7 ; // 0=coordinator, 1=router, 2=end device
2020-05-17 17:33:42 +01:00
const char * deviceTypeStr ;
switch ( deviceType ) {
case 0 : deviceTypeStr = PSTR ( " Coordinator " ) ; break ;
case 1 : deviceTypeStr = PSTR ( " Router " ) ; break ;
case 2 : deviceTypeStr = PSTR ( " Device " ) ; break ;
default : deviceTypeStr = PSTR ( " Unknown " ) ; break ;
}
2019-09-29 14:38:26 +01:00
bool complexDescriptorAvailable = ( logicalType & 0x08 ) ? 1 : 0 ;
2019-11-09 08:25:15 +00:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2019-09-29 14:38:26 +01:00
" \" Status \" :%d, \" NodeType \" : \" %s \" , \" ComplexDesc \" :%s}} " ) ,
2020-05-17 17:33:42 +01:00
ZIGBEE_STATUS_NODE_DESC , deviceTypeStr ,
complexDescriptorAvailable ? PSTR ( " true " ) : PSTR ( " false " )
2019-09-29 14:38:26 +01:00
) ;
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
2019-09-29 14:38:26 +01:00
}
return - 1 ;
}
2020-03-23 21:46:26 +00:00
//
// Porcess Receive Active Endpoint
//
2021-01-25 15:02:56 +00:00
int32_t Z_ReceiveActiveEp ( int32_t res , const SBuffer & buf ) {
2019-09-29 14:38:26 +01:00
// Received ZDO_ACTIVE_EP_RSP
2020-07-02 21:56:37 +01:00
# ifdef USE_ZIGBEE_ZNP
// Z_ShortAddress srcAddr = buf.get16(2);
2020-11-12 18:38:21 +00:00
// uint8_t status = buf.get8(4);
2019-09-29 14:38:26 +01:00
Z_ShortAddress nwkAddr = buf . get16 ( 5 ) ;
uint8_t activeEpCount = buf . get8 ( 7 ) ;
uint8_t * activeEpList = ( uint8_t * ) buf . charptr ( 8 ) ;
2020-07-02 21:56:37 +01:00
# endif
# ifdef USE_ZIGBEE_EZSP
2020-11-12 18:38:21 +00:00
// uint8_t status = buf.get8(0);
2020-07-02 21:56:37 +01:00
Z_ShortAddress nwkAddr = buf . get16 ( 1 ) ;
uint8_t activeEpCount = buf . get8 ( 3 ) ;
uint8_t * activeEpList = ( uint8_t * ) buf . charptr ( 4 ) ;
# endif
2019-09-29 14:38:26 +01:00
2020-11-22 15:07:09 +00:00
// device is reachable
zigbee_devices . deviceWasReached ( nwkAddr ) ;
2019-09-29 14:38:26 +01:00
for ( uint32_t i = 0 ; i < activeEpCount ; i + + ) {
2020-09-12 09:57:54 +01:00
uint8_t ep = activeEpList [ i ] ;
2020-10-31 18:51:17 +00:00
zigbee_devices . getShortAddr ( nwkAddr ) . addEndpoint ( ep ) ;
2021-08-22 21:44:21 +01:00
if ( ( i < USE_ZIGBEE_AUTOBIND_MAX_ENDPOINTS ) & & ( ep < USE_ZIGBEE_AUTOBIND_MAX_CLUSTER ) ) {
2020-09-12 09:57:54 +01:00
zigbee_devices . queueTimer ( nwkAddr , 0 /* groupaddr */ , 1500 , ep /* fake cluster as ep */ , ep , Z_CAT_EP_DESC , 0 /* value */ , & Z_SendSimpleDescReq ) ;
}
2019-09-29 14:38:26 +01:00
}
2019-11-09 08:25:15 +00:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2019-09-29 14:38:26 +01:00
" \" Status \" :%d, \" ActiveEndpoints \" :[ " ) ,
ZIGBEE_STATUS_ACTIVE_EP ) ;
for ( uint32_t i = 0 ; i < activeEpCount ; i + + ) {
if ( i > 0 ) { ResponseAppend_P ( PSTR ( " , " ) ) ; }
ResponseAppend_P ( PSTR ( " \" 0x%02X \" " ) , activeEpList [ i ] ) ;
}
ResponseAppend_P ( PSTR ( " ]}} " ) ) ;
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
2020-03-19 08:43:04 +00:00
2020-09-12 09:57:54 +01:00
Z_SendDeviceInfoRequest ( nwkAddr ) ; // probe for ModelId and ManufId
return - 1 ;
}
// list of clusters that need bindings
const uint8_t Z_bindings [ ] PROGMEM = {
2021-02-18 15:27:03 +00:00
Cx0001 , Cx0006 , Cx0008 , Cx0102 , Cx0201 , Cx0300 ,
2020-09-12 09:57:54 +01:00
Cx0400 , Cx0402 , Cx0403 , Cx0405 , Cx0406 ,
Cx0500 ,
} ;
int32_t Z_ClusterToCxBinding ( uint16_t cluster ) {
uint8_t cx = ClusterToCx ( cluster ) ;
2021-02-28 11:50:02 +00:00
for ( uint32_t i = 0 ; i < nitems ( Z_bindings ) ; i + + ) {
2020-09-12 09:57:54 +01:00
if ( pgm_read_byte ( & Z_bindings [ i ] ) = = cx ) {
return i ;
}
}
return - 1 ;
}
void Z_AutoBindDefer ( uint16_t shortaddr , uint8_t endpoint , const SBuffer & buf ,
size_t in_index , size_t in_len , size_t out_index , size_t out_len ) {
// We use bitmaps to mark clusters that need binding and config attributes
// All clusters in 'in' and 'out' are bounded
// Only cluster in 'in' receive configure attribute requests
uint32_t cluster_map = 0 ; // max 32 clusters to bind
uint32_t cluster_in_map = 0 ; // map of clusters only in 'in' group, to be bounded
// First enumerate all clusters to bind, from in or out clusters
// scan in clusters
for ( uint32_t i = 0 ; i < in_len ; i + + ) {
uint16_t cluster = buf . get16 ( in_index + i * 2 ) ;
uint32_t found_cx = Z_ClusterToCxBinding ( cluster ) ; // convert to Cx of -1 if not found
if ( found_cx > = 0 ) {
bitSet ( cluster_map , found_cx ) ;
bitSet ( cluster_in_map , found_cx ) ;
}
}
// scan out clusters
for ( uint32_t i = 0 ; i < out_len ; i + + ) {
uint16_t cluster = buf . get16 ( out_index + i * 2 ) ;
uint32_t found_cx = Z_ClusterToCxBinding ( cluster ) ; // convert to Cx of -1 if not found
if ( found_cx > = 0 ) {
bitSet ( cluster_map , found_cx ) ;
}
}
// if IAS device, request the device type
if ( bitRead ( cluster_map , Z_ClusterToCxBinding ( 0x0500 ) ) ) {
// send a read command to cluster 0x0500, attribute 0x0001 (ZoneType) - to read the type of sensor
zigbee_devices . queueTimer ( shortaddr , 0 /* groupaddr */ , 2000 , 0x0500 , endpoint , Z_CAT_READ_ATTRIBUTE , 0x0001 , & Z_SendSingleAttributeRead ) ;
2020-12-21 11:13:57 +00:00
zigbee_devices . queueTimer ( shortaddr , 0 /* groupaddr */ , 2000 , 0x0500 , endpoint , Z_CAT_CIE_ATTRIBUTE , 0 /* value */ , & Z_WriteCIEAddress ) ;
zigbee_devices . queueTimer ( shortaddr , 0 /* groupaddr */ , 2000 , 0x0500 , endpoint , Z_CAT_CIE_ENROLL , 1 /* zone */ , & Z_SendCIEZoneEnrollResponse ) ;
2020-09-12 09:57:54 +01:00
}
// enqueue bind requests
2021-02-28 11:50:02 +00:00
for ( uint32_t i = 0 ; i < nitems ( Z_bindings ) ; i + + ) {
2020-09-12 09:57:54 +01:00
if ( bitRead ( cluster_map , i ) ) {
uint16_t cluster = CxToCluster ( pgm_read_byte ( & Z_bindings [ i ] ) ) ;
2020-11-07 13:34:23 +00:00
if ( ( cluster = = 0x0001 ) & & ( ! Z_BatteryReportingDeviceSpecific ( shortaddr ) ) ) { continue ; }
2020-09-12 09:57:54 +01:00
zigbee_devices . queueTimer ( shortaddr , 0 /* groupaddr */ , 2000 , cluster , endpoint , Z_CAT_BIND , 0 /* value */ , & Z_AutoBind ) ;
}
}
// enqueue config attribute requests
2021-02-28 11:50:02 +00:00
for ( uint32_t i = 0 ; i < nitems ( Z_bindings ) ; i + + ) {
2020-09-12 09:57:54 +01:00
if ( bitRead ( cluster_in_map , i ) ) {
uint16_t cluster = CxToCluster ( pgm_read_byte ( & Z_bindings [ i ] ) ) ;
2020-11-07 13:34:23 +00:00
if ( ( cluster = = 0x0001 ) & & ( ! Z_BatteryReportingDeviceSpecific ( shortaddr ) ) ) { continue ; }
2020-09-12 09:57:54 +01:00
zigbee_devices . queueTimer ( shortaddr , 0 /* groupaddr */ , 2000 , cluster , endpoint , Z_CAT_CONFIG_ATTR , 0 /* value */ , & Z_AutoConfigReportingForCluster ) ;
}
}
}
2021-01-25 15:02:56 +00:00
int32_t Z_ReceiveSimpleDesc ( int32_t res , const SBuffer & buf ) {
2020-09-12 09:57:54 +01:00
# ifdef USE_ZIGBEE_ZNP
// Received ZDO_SIMPLE_DESC_RSP
// Z_ShortAddress srcAddr = buf.get16(2);
uint8_t status = buf . get8 ( 4 ) ;
Z_ShortAddress nwkAddr = buf . get16 ( 5 ) ;
2020-11-12 18:38:21 +00:00
// uint8_t lenDescriptor = buf.get8(7);
2020-09-12 09:57:54 +01:00
uint8_t endpoint = buf . get8 ( 8 ) ;
uint16_t profileId = buf . get16 ( 9 ) ; // The profile Id for this endpoint.
uint16_t deviceId = buf . get16 ( 11 ) ; // The Device Description Id for this endpoint.
uint8_t deviceVersion = buf . get8 ( 13 ) ; // 0 – Version 1.00
uint8_t numInCluster = buf . get8 ( 14 ) ;
uint8_t numOutCluster = buf . get8 ( 15 + numInCluster * 2 ) ;
const size_t numInIndex = 15 ;
const size_t numOutIndex = 16 + numInCluster * 2 ;
# endif
# ifdef USE_ZIGBEE_EZSP
uint8_t status = buf . get8 ( 0 ) ;
Z_ShortAddress nwkAddr = buf . get16 ( 1 ) ;
2020-11-12 18:38:21 +00:00
// uint8_t lenDescriptor = buf.get8(3);
2020-09-12 09:57:54 +01:00
uint8_t endpoint = buf . get8 ( 4 ) ;
uint16_t profileId = buf . get16 ( 5 ) ; // The profile Id for this endpoint.
uint16_t deviceId = buf . get16 ( 7 ) ; // The Device Description Id for this endpoint.
uint8_t deviceVersion = buf . get8 ( 9 ) ; // 0 – Version 1.00
uint8_t numInCluster = buf . get8 ( 10 ) ;
uint8_t numOutCluster = buf . get8 ( 11 + numInCluster * 2 ) ;
const size_t numInIndex = 11 ;
const size_t numOutIndex = 12 + numInCluster * 2 ;
# endif
2020-12-06 18:20:42 +00:00
bool is_tuya_protocol = false ;
2020-09-12 09:57:54 +01:00
if ( 0 = = status ) {
2020-11-22 15:07:09 +00:00
// device is reachable
zigbee_devices . deviceWasReached ( nwkAddr ) ;
2021-06-11 17:14:12 +01:00
if ( ! Settings - > flag4 . zb_disable_autobind ) {
2020-09-12 09:57:54 +01:00
Z_AutoBindDefer ( nwkAddr , endpoint , buf , numInIndex , numInCluster , numOutIndex , numOutCluster ) ;
}
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2020-12-06 18:20:42 +00:00
" \" Status \" :%d, \" Device \" : \" 0x%04X \" , \" Endpoint \" : \" 0x%02X \" "
2020-09-12 09:57:54 +01:00
" , \" ProfileId \" : \" 0x%04X \" , \" DeviceId \" : \" 0x%04X \" , \" DeviceVersion \" :%d "
2020-11-08 10:12:02 +00:00
" , \" InClusters \" :[ " ) ,
2020-12-06 18:20:42 +00:00
ZIGBEE_STATUS_SIMPLE_DESC , nwkAddr , endpoint ,
2020-09-12 09:57:54 +01:00
profileId , deviceId , deviceVersion ) ;
for ( uint32_t i = 0 ; i < numInCluster ; i + + ) {
if ( i > 0 ) { ResponseAppend_P ( PSTR ( " , " ) ) ; }
ResponseAppend_P ( PSTR ( " \" 0x%04X \" " ) , buf . get16 ( numInIndex + i * 2 ) ) ;
2020-12-06 18:20:42 +00:00
if ( buf . get16 ( numInIndex + i * 2 ) = = 0xEF00 ) { is_tuya_protocol = true ; } // tuya protocol
2020-09-12 09:57:54 +01:00
}
ResponseAppend_P ( PSTR ( " ], \" OutClusters \" :[ " ) ) ;
for ( uint32_t i = 0 ; i < numOutCluster ; i + + ) {
if ( i > 0 ) { ResponseAppend_P ( PSTR ( " , " ) ) ; }
ResponseAppend_P ( PSTR ( " \" 0x%04X \" " ) , buf . get16 ( numOutIndex + i * 2 ) ) ;
}
ResponseAppend_P ( PSTR ( " ]}} " ) ) ;
2021-04-07 14:07:05 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
2020-09-12 09:57:54 +01:00
}
2020-03-19 08:43:04 +00:00
2020-12-06 18:20:42 +00:00
// If tuya protocol, change the model information
if ( is_tuya_protocol ) {
Z_Device & device = zigbee_devices . getShortAddr ( nwkAddr ) ;
device . addEndpoint ( endpoint ) ;
device . data . get < Z_Data_Mode > ( endpoint ) . setConfig ( ZM_Tuya ) ;
zigbee_devices . dirty ( ) ;
}
2019-09-29 14:38:26 +01:00
return - 1 ;
}
2020-03-23 21:46:26 +00:00
//
// Handle IEEEAddr incoming message
//
2020-07-02 21:56:37 +01:00
// Same works for both ZNP and EZSP
2021-01-25 15:02:56 +00:00
int32_t Z_ReceiveIEEEAddr ( int32_t res , const SBuffer & buf ) {
2020-07-02 21:56:37 +01:00
# ifdef USE_ZIGBEE_ZNP
2020-02-23 15:46:00 +00:00
uint8_t status = buf . get8 ( 2 ) ;
Z_IEEEAddress ieeeAddr = buf . get64 ( 3 ) ;
Z_ShortAddress nwkAddr = buf . get16 ( 11 ) ;
2020-03-23 21:46:26 +00:00
// uint8_t startIndex = buf.get8(13); // not used
2020-02-23 15:46:00 +00:00
// uint8_t numAssocDev = buf.get8(14);
2020-07-02 21:56:37 +01:00
# endif // USE_ZIGBEE_ZNP
# ifdef USE_ZIGBEE_EZSP
uint8_t status = buf . get8 ( 0 ) ;
Z_IEEEAddress ieeeAddr = buf . get64 ( 1 ) ;
Z_ShortAddress nwkAddr = buf . get16 ( 9 ) ;
// uint8_t numAssocDev = buf.get8(11);
// uint8_t startIndex = buf.get8(12); // not used
# endif // USE_ZIGBEE_EZSP
2020-02-23 15:46:00 +00:00
if ( 0 = = status ) { // SUCCESS
zigbee_devices . updateDevice ( nwkAddr , ieeeAddr ) ;
2020-11-22 15:07:09 +00:00
zigbee_devices . deviceWasReached ( nwkAddr ) ;
2020-02-23 15:46:00 +00:00
// Ping response
2020-03-14 13:17:30 +00:00
const char * friendlyName = zigbee_devices . getFriendlyName ( nwkAddr ) ;
2020-05-17 17:33:42 +01:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_PING " \" :{ \" " D_JSON_ZIGBEE_DEVICE " \" : \" 0x%04X \" "
2021-01-24 15:35:36 +00:00
" , \" " D_JSON_ZIGBEE_IEEE " \" : \" 0x%_X \" " ) , nwkAddr , & ieeeAddr ) ;
2020-02-23 15:46:00 +00:00
if ( friendlyName ) {
2020-05-17 17:33:42 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_ZIGBEE_NAME " \" : \" %s \" " ) , friendlyName ) ;
2020-02-23 15:46:00 +00:00
}
2020-11-17 18:35:06 +00:00
ResponseAppend_P ( PSTR ( " }} " ) ) ;
2020-02-23 15:46:00 +00:00
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
2020-02-23 15:46:00 +00:00
}
return - 1 ;
}
2020-03-14 13:17:30 +00:00
//
// Report any AF_DATA_CONFIRM message
// Ex: {"ZbConfirm":{"Endpoint":1,"Status":0,"StatusMessage":"SUCCESS"}}
//
2021-01-25 15:02:56 +00:00
int32_t ZNP_DataConfirm ( int32_t res , const SBuffer & buf ) {
2020-03-14 13:17:30 +00:00
uint8_t status = buf . get8 ( 2 ) ;
uint8_t endpoint = buf . get8 ( 3 ) ;
2020-03-23 21:46:26 +00:00
//uint8_t transId = buf.get8(4); // unused
2020-03-14 13:17:30 +00:00
if ( status ) { // only report errors
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_CONFIRM " \" :{ \" " D_CMND_ZIGBEE_ENDPOINT " \" :%d "
" , \" " D_JSON_ZIGBEE_STATUS " \" :%d "
" , \" " D_JSON_ZIGBEE_STATUS_MSG " \" : \" %s \" "
2020-03-23 21:46:26 +00:00
" }} " ) , endpoint , status , getZigbeeStatusMessage ( status ) . c_str ( ) ) ;
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
2020-03-14 13:17:30 +00:00
}
return - 1 ;
}
2020-05-11 20:16:17 +01:00
//
// Handle State Change Indication incoming message
//
2020-05-12 20:13:46 +01:00
// Reference:
// 0x00: Initialized - not started automatically
// 0x01: Initialized - not connected to anything
// 0x02: Discovering PAN's to join
// 0x03: Joining a PAN
// 0x04: Rejoining a PAN, only for end devices
// 0x05: Joined but not yet authenticated by trust center
// 0x06: Started as device after authentication
// 0x07: Device joined, authenticated and is a router
// 0x08: Starting as ZigBee Coordinator
// 0x09: Started as ZigBee Coordinator
// 0x0A: Device has lost information about its parent
2021-01-25 15:02:56 +00:00
int32_t ZNP_ReceiveStateChange ( int32_t res , const SBuffer & buf ) {
2020-05-11 20:16:17 +01:00
uint8_t state = buf . get8 ( 2 ) ;
2020-05-12 20:13:46 +01:00
const char * msg = nullptr ;
switch ( state ) {
case ZDO_DEV_NWK_DISC : // 0x02
msg = PSTR ( " Scanning Zigbee network " ) ;
break ;
case ZDO_DEV_NWK_JOINING : // 0x03
case ZDO_DEV_NWK_REJOIN : // 0x04
msg = PSTR ( " Joining a PAN " ) ;
break ;
case ZDO_DEV_END_DEVICE_UNAUTH : // 0x05
msg = PSTR ( " Joined, not yet authenticated " ) ;
break ;
case ZDO_DEV_END_DEVICE : // 0x06
msg = PSTR ( " Started as device " ) ;
break ;
case ZDO_DEV_ROUTER : // 0x07
msg = PSTR ( " Started as router " ) ;
break ;
case ZDO_DEV_ZB_COORD : // 0x09
msg = PSTR ( " Started as coordinator " ) ;
break ;
case ZDO_DEV_NWK_ORPHAN : // 0x0A
msg = PSTR ( " Device has lost its parent " ) ;
break ;
} ;
if ( msg ) {
2020-05-11 20:16:17 +01:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2020-05-12 20:13:46 +01:00
" \" Status \" :%d, \" NewState \" :%d, \" Message \" : \" %s \" }} " ) ,
ZIGBEE_STATUS_SCANNING , state , msg
2020-05-11 20:16:17 +01:00
) ;
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
2020-05-11 20:16:17 +01:00
}
2020-05-12 20:13:46 +01:00
if ( ( ZDO_DEV_END_DEVICE = = state ) | | ( ZDO_DEV_ROUTER = = state ) | | ( ZDO_DEV_ZB_COORD = = state ) ) {
return 0 ; // device sucessfully started
} else {
return - 1 ; // ignore
}
2020-05-11 20:16:17 +01:00
}
2020-03-23 21:46:26 +00:00
//
// Handle Receive End Device Announce incoming message
2020-03-26 18:34:59 +00:00
// This message is also received when a previously paired device is powered up
2020-03-23 21:46:26 +00:00
// Send back Active Ep Req message
//
2021-01-25 15:02:56 +00:00
int32_t Z_ReceiveEndDeviceAnnonce ( int32_t res , const SBuffer & buf ) {
2020-06-29 21:21:32 +01:00
# ifdef USE_ZIGBEE_ZNP
// Z_ShortAddress srcAddr = buf.get16(2);
2019-09-29 14:38:26 +01:00
Z_ShortAddress nwkAddr = buf . get16 ( 4 ) ;
Z_IEEEAddress ieeeAddr = buf . get64 ( 6 ) ;
uint8_t capabilities = buf . get8 ( 14 ) ;
2020-06-29 21:21:32 +01:00
# endif
# ifdef USE_ZIGBEE_EZSP
// uint8_t seq = buf.get8(0);
2020-07-02 21:56:37 +01:00
Z_ShortAddress nwkAddr = buf . get16 ( 0 ) ;
Z_IEEEAddress ieeeAddr = buf . get64 ( 2 ) ;
uint8_t capabilities = buf . get8 ( 10 ) ;
2020-06-29 21:21:32 +01:00
# endif
2019-09-29 14:38:26 +01:00
2021-01-22 17:17:40 +00:00
// record if we already knew the ieeeAddr for this device
// this will influence the decision whether we do auto-binding or not
const Z_Device & device_before = zigbee_devices . findShortAddr ( nwkAddr ) ;
bool ieee_already_known = false ;
if ( device_before . valid ( ) & & ( device_before . longaddr ! = 0 ) & & ( device_before . longaddr = = ieeeAddr ) ) {
ieee_already_known = true ;
}
2019-10-06 11:40:58 +01:00
zigbee_devices . updateDevice ( nwkAddr , ieeeAddr ) ;
2020-11-22 15:07:09 +00:00
// device is reachable
zigbee_devices . deviceWasReached ( nwkAddr ) ;
2019-09-29 14:38:26 +01:00
2019-11-09 08:25:15 +00:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2021-01-24 15:35:36 +00:00
" \" Status \" :%d, \" IEEEAddr \" : \" 0x%_X \" , \" ShortAddr \" : \" 0x%04X \" "
2019-09-29 14:38:26 +01:00
" , \" PowerSource \" :%s, \" ReceiveWhenIdle \" :%s, \" Security \" :%s}} " ) ,
2021-01-24 15:35:36 +00:00
ZIGBEE_STATUS_DEVICE_ANNOUNCE , & ieeeAddr , nwkAddr ,
2020-05-17 17:33:42 +01:00
( capabilities & 0x04 ) ? PSTR ( " true " ) : PSTR ( " false " ) ,
( capabilities & 0x08 ) ? PSTR ( " true " ) : PSTR ( " false " ) ,
( capabilities & 0x40 ) ? PSTR ( " true " ) : PSTR ( " false " )
2019-09-29 14:38:26 +01:00
) ;
2020-03-26 18:34:59 +00:00
// query the state of the bulb (for Alexa)
uint32_t wait_ms = 2000 ; // wait for 2s
Z_Query_Bulb ( nwkAddr , wait_ms ) ;
2019-09-29 14:38:26 +01:00
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
2021-01-22 17:17:40 +00:00
// Continue the discovery process and auto-binding only if the device was unknown or if PermitJoin is ongoing
if ( ! ieee_already_known | | zigbee . permit_end_time ) {
Z_SendActiveEpReq ( nwkAddr ) ;
}
2019-09-29 14:38:26 +01:00
return - 1 ;
}
2020-03-23 21:46:26 +00:00
//
// Handle Receive TC Dev Ind incoming message
2019-12-23 15:53:54 +00:00
// 45CA
2020-03-23 21:46:26 +00:00
//
2021-01-25 15:02:56 +00:00
int32_t ZNP_ReceiveTCDevInd ( int32_t res , const SBuffer & buf ) {
2019-12-23 15:53:54 +00:00
Z_ShortAddress srcAddr = buf . get16 ( 2 ) ;
Z_IEEEAddress ieeeAddr = buf . get64 ( 4 ) ;
Z_ShortAddress parentNw = buf . get16 ( 12 ) ;
zigbee_devices . updateDevice ( srcAddr , ieeeAddr ) ;
2020-11-22 15:07:09 +00:00
// device is reachable
zigbee_devices . deviceWasReached ( srcAddr ) ;
2019-12-23 15:53:54 +00:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2021-01-24 15:35:36 +00:00
" \" Status \" :%d, \" IEEEAddr \" : \" 0x%_X \" , \" ShortAddr \" : \" 0x%04X \" "
2019-12-23 15:53:54 +00:00
" , \" ParentNetwork \" : \" 0x%04X \" }} " ) ,
2021-01-24 15:35:36 +00:00
ZIGBEE_STATUS_DEVICE_INDICATION , & ieeeAddr , srcAddr , parentNw
2019-12-23 15:53:54 +00:00
) ;
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
2019-12-23 15:53:54 +00:00
return - 1 ;
}
2020-03-23 21:46:26 +00:00
//
// Handle Bind Rsp incoming message
//
2021-01-25 15:02:56 +00:00
int32_t Z_BindRsp ( int32_t res , const SBuffer & buf ) {
2020-07-05 20:01:26 +01:00
# ifdef USE_ZIGBEE_ZNP
2020-03-23 21:46:26 +00:00
Z_ShortAddress nwkAddr = buf . get16 ( 2 ) ;
uint8_t status = buf . get8 ( 4 ) ;
2020-07-05 20:01:26 +01:00
String msg = getZigbeeStatusMessage ( status ) ;
# endif // USE_ZIGBEE_ZNP
# ifdef USE_ZIGBEE_EZSP
uint8_t status = buf . get8 ( 0 ) ;
Z_ShortAddress nwkAddr = buf . get16 ( buf . len ( ) - 2 ) ; // last 2 bytes
String msg = getZDPStatusMessage ( status ) ;
# endif // USE_ZIGBEE_EZSP
2020-03-23 21:46:26 +00:00
2020-11-22 15:07:09 +00:00
// device is reachable
zigbee_devices . deviceWasReached ( nwkAddr ) ;
2020-03-23 21:46:26 +00:00
const char * friendlyName = zigbee_devices . getFriendlyName ( nwkAddr ) ;
2020-05-17 17:33:42 +01:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_BIND " \" :{ \" " D_JSON_ZIGBEE_DEVICE " \" : \" 0x%04X \" " ) , nwkAddr ) ;
2020-03-23 21:46:26 +00:00
if ( friendlyName ) {
2020-05-17 17:33:42 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_ZIGBEE_NAME " \" : \" %s \" " ) , friendlyName ) ;
2020-03-23 21:46:26 +00:00
}
2020-05-17 17:33:42 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_ZIGBEE_STATUS " \" :%d "
" , \" " D_JSON_ZIGBEE_STATUS_MSG " \" : \" %s \" "
2020-07-05 20:01:26 +01:00
" }} " ) , status , msg . c_str ( ) ) ;
2020-05-17 17:33:42 +01:00
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
2020-03-23 21:46:26 +00:00
return - 1 ;
}
2020-03-30 18:23:06 +01:00
2020-03-26 19:58:59 +00:00
//
// Handle Unbind Rsp incoming message
//
2021-01-25 15:02:56 +00:00
int32_t Z_UnbindRsp ( int32_t res , const SBuffer & buf ) {
2020-07-05 20:01:26 +01:00
# ifdef USE_ZIGBEE_ZNP
2020-03-26 19:58:59 +00:00
Z_ShortAddress nwkAddr = buf . get16 ( 2 ) ;
uint8_t status = buf . get8 ( 4 ) ;
2020-07-05 20:01:26 +01:00
String msg = getZigbeeStatusMessage ( status ) ;
# endif // USE_ZIGBEE_ZNP
# ifdef USE_ZIGBEE_EZSP
uint8_t status = buf . get8 ( 0 ) ;
Z_ShortAddress nwkAddr = buf . get16 ( buf . len ( ) - 2 ) ; // last 2 bytes
String msg = getZDPStatusMessage ( status ) ;
# endif // USE_ZIGBEE_EZSP
2020-03-26 19:58:59 +00:00
2020-11-22 15:07:09 +00:00
// device is reachable
zigbee_devices . deviceWasReached ( nwkAddr ) ;
2020-03-26 19:58:59 +00:00
const char * friendlyName = zigbee_devices . getFriendlyName ( nwkAddr ) ;
2020-05-17 17:33:42 +01:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_UNBIND " \" :{ \" " D_JSON_ZIGBEE_DEVICE " \" : \" 0x%04X \" " ) , nwkAddr ) ;
2020-03-26 19:58:59 +00:00
if ( friendlyName ) {
2020-05-17 17:33:42 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_ZIGBEE_NAME " \" : \" %s \" " ) , friendlyName ) ;
2020-03-26 19:58:59 +00:00
}
2020-07-05 20:01:26 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_ZIGBEE_STATUS " \" :%d "
" , \" " D_JSON_ZIGBEE_STATUS_MSG " \" : \" %s \" "
" }} " ) , status , msg . c_str ( ) ) ;
2020-05-17 17:33:42 +01:00
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
2020-03-26 19:58:59 +00:00
return - 1 ;
}
2020-10-28 16:30:46 +00:00
2020-03-30 18:23:06 +01:00
//
// Handle MgMt Bind Rsp incoming message
//
2021-01-25 15:02:56 +00:00
int32_t Z_MgmtBindRsp ( int32_t res , const SBuffer & buf ) {
2020-11-30 18:12:16 +00:00
return Z_Mgmt_Lqi_Bind_Rsp ( res , buf , false ) ;
2020-03-30 18:23:06 +01:00
}
2020-03-23 21:46:26 +00:00
2020-10-28 16:30:46 +00:00
// Return false, true or null (if unknown)
const char * TrueFalseNull ( uint32_t value ) {
if ( value = = 0 ) {
return PSTR ( " false " ) ;
} else if ( value = = 1 ) {
return PSTR ( " true " ) ;
} else {
return PSTR ( " null " ) ;
}
}
const char * Z_DeviceRelationship ( uint32_t value ) {
switch ( value ) {
case 0 : return PSTR ( " Parent " ) ;
case 1 : return PSTR ( " Child " ) ;
case 2 : return PSTR ( " Sibling " ) ;
case 4 : return PSTR ( " Previous " ) ;
case 3 :
default : return PSTR ( " None " ) ;
}
}
const char * Z_DeviceType ( uint32_t value ) {
switch ( value ) {
case 0 : return PSTR ( " Coordinator " ) ;
case 1 : return PSTR ( " Router " ) ;
case 2 : return PSTR ( " Device " ) ;
default : return PSTR ( " Unknown " ) ;
}
}
//
2020-11-30 18:12:16 +00:00
// Combined code for MgmtLqiRsp and MgmtBindRsp
2020-10-28 16:30:46 +00:00
//
2020-11-30 18:12:16 +00:00
// If the response has a follow-up, send more requests automatically
//
2021-01-25 15:02:56 +00:00
int32_t Z_Mgmt_Lqi_Bind_Rsp ( int32_t res , const SBuffer & buf , boolean lqi ) {
2020-10-28 16:30:46 +00:00
# ifdef USE_ZIGBEE_ZNP
uint16_t shortaddr = buf . get16 ( 2 ) ;
uint8_t status = buf . get8 ( 4 ) ;
2020-11-30 18:12:16 +00:00
uint8_t total = buf . get8 ( 5 ) ;
uint8_t start = buf . get8 ( 6 ) ;
uint8_t len = buf . get8 ( 7 ) ;
2020-10-28 16:30:46 +00:00
const size_t prefix_len = 8 ;
# endif // USE_ZIGBEE_ZNP
# ifdef USE_ZIGBEE_EZSP
uint16_t shortaddr = buf . get16 ( buf . len ( ) - 2 ) ;
uint8_t status = buf . get8 ( 0 ) ;
2020-11-30 18:12:16 +00:00
uint8_t total = buf . get8 ( 1 ) ;
uint8_t start = buf . get8 ( 2 ) ;
uint8_t len = buf . get8 ( 3 ) ;
2020-10-28 16:30:46 +00:00
const size_t prefix_len = 4 ;
# endif // USE_ZIGBEE_EZSP
2020-11-22 15:07:09 +00:00
// device is reachable
zigbee_devices . deviceWasReached ( shortaddr ) ;
2020-12-13 18:18:36 +00:00
bool non_empty = false ; // check whether the response contains relevant information
2020-11-22 15:07:09 +00:00
2020-10-28 16:30:46 +00:00
const char * friendlyName = zigbee_devices . getFriendlyName ( shortaddr ) ;
2020-11-30 18:12:16 +00:00
Response_P ( PSTR ( " { \" %s \" :{ \" " D_JSON_ZIGBEE_DEVICE " \" : \" 0x%04X \" " ) ,
lqi ? PSTR ( D_JSON_ZIGBEE_MAP ) : PSTR ( D_JSON_ZIGBEE_BIND_STATE ) , shortaddr ) ;
2020-10-28 16:30:46 +00:00
if ( friendlyName ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_ZIGBEE_NAME " \" : \" %s \" " ) , friendlyName ) ;
}
ResponseAppend_P ( PSTR ( " , \" " D_JSON_ZIGBEE_STATUS " \" :%d "
" , \" " D_JSON_ZIGBEE_STATUS_MSG " \" : \" %s \" "
2020-11-30 18:12:16 +00:00
" , \" Total \" :%d "
" , \" Start \" :%d "
" , \" %s \" :[ "
) , status , getZigbeeStatusMessage ( status ) . c_str ( ) , total , start + 1 ,
lqi ? PSTR ( " Map " ) : PSTR ( " Bindings " ) ) ;
if ( lqi ) {
uint32_t idx = prefix_len ;
for ( uint32_t i = 0 ; i < len ; i + + ) {
if ( idx + 22 > buf . len ( ) ) { break ; } // size 22 for EZSP
//uint64_t extpanid = buf.get16(idx); // unused
// uint64_t m_longaddr = buf.get64(idx + 8);
uint16_t m_shortaddr = buf . get16 ( idx + 16 ) ;
uint8_t m_dev_type = buf . get8 ( idx + 18 ) ;
uint8_t m_permitjoin = buf . get8 ( idx + 19 ) ;
uint8_t m_depth = buf . get8 ( idx + 20 ) ;
uint8_t m_lqi = buf . get8 ( idx + 21 ) ;
idx + = 22 ;
2020-12-13 18:18:36 +00:00
non_empty = true ;
2020-11-30 18:12:16 +00:00
if ( i > 0 ) {
ResponseAppend_P ( PSTR ( " , " ) ) ;
}
ResponseAppend_P ( PSTR ( " { \" Device \" : \" 0x%04X \" , " ) , m_shortaddr ) ;
2020-10-28 16:30:46 +00:00
2020-11-30 18:12:16 +00:00
const char * friendlyName = zigbee_devices . getFriendlyName ( m_shortaddr ) ;
if ( friendlyName ) {
ResponseAppend_P ( PSTR ( " \" Name \" : \" %s \" , " ) , friendlyName ) ;
}
ResponseAppend_P ( PSTR ( " \" DeviceType \" : \" %s \" , "
" \" RxOnWhenIdle \" :%s, "
" \" Relationship \" : \" %s \" , "
" \" PermitJoin \" :%s, "
" \" Depth \" :%d, "
" \" LinkQuality \" :%d "
" } "
) ,
Z_DeviceType ( m_dev_type & 0x03 ) ,
TrueFalseNull ( ( m_dev_type & 0x0C ) > > 2 ) ,
Z_DeviceRelationship ( ( m_dev_type & 0x70 ) > > 4 ) ,
TrueFalseNull ( m_permitjoin & 0x02 ) ,
m_depth ,
m_lqi ) ;
2020-12-12 18:05:47 +00:00
// detect any router
Z_Device & device = zigbee_devices . findShortAddr ( m_shortaddr ) ;
if ( device . valid ( ) ) {
if ( ( m_dev_type & 0x03 ) = = 1 ) { // it is a router
device . setRouter ( true ) ;
}
}
// Add information to zigbee mapper
// Z_Mapper_Edge::Edge_Type edge_type;
// switch ((m_dev_type & 0x70) >> 4) {
// case 0: edge_type = Z_Mapper_Edge::Parent; break;
// case 1: edge_type = Z_Mapper_Edge::Child; break;
// case 2: edge_type = Z_Mapper_Edge::Sibling; break;
// default: edge_type = Z_Mapper_Edge::Unknown; break;
// }
// Z_Mapper_Edge edge(m_shortaddr, shortaddr, m_lqi, edge_type);
Z_Mapper_Edge edge ( m_shortaddr , shortaddr , m_lqi ) ;
zigbee_mapper . addEdge ( edge ) ;
2020-10-28 16:30:46 +00:00
}
2020-11-30 18:12:16 +00:00
ResponseAppend_P ( PSTR ( " ]}} " ) ) ;
} else { // Bind
uint32_t idx = prefix_len ;
for ( uint32_t i = 0 ; i < len ; i + + ) {
if ( idx + 14 > buf . len ( ) ) { break ; } // overflow, frame size is between 14 and 21
//uint64_t srcaddr = buf.get16(idx); // unused
uint8_t srcep = buf . get8 ( idx + 8 ) ;
uint16_t cluster = buf . get16 ( idx + 9 ) ;
uint8_t addrmode = buf . get8 ( idx + 11 ) ;
uint16_t group = 0x0000 ;
uint64_t dstaddr = 0 ;
uint8_t dstep = 0x00 ;
if ( Z_Addr_Group = = addrmode ) { // Group address mode
group = buf . get16 ( idx + 12 ) ;
idx + = 14 ;
} else if ( Z_Addr_IEEEAddress = = addrmode ) { // IEEE address mode
dstaddr = buf . get64 ( idx + 12 ) ;
dstep = buf . get8 ( idx + 20 ) ;
idx + = 21 ;
} else {
2021-06-05 10:47:09 +01:00
//AddLog(LOG_LEVEL_INFO, PSTR("ZNP_MgmtBindRsp unknwon address mode %d"), addrmode);
2020-11-30 18:12:16 +00:00
break ; // abort for any other value since we don't know the length of the field
}
2020-12-13 18:18:36 +00:00
non_empty = true ;
2020-11-30 18:12:16 +00:00
if ( i > 0 ) {
ResponseAppend_P ( PSTR ( " , " ) ) ;
}
ResponseAppend_P ( PSTR ( " { \" Cluster \" : \" 0x%04X \" , \" Endpoint \" :%d, " ) , cluster , srcep ) ;
if ( Z_Addr_Group = = addrmode ) { // Group address mode
ResponseAppend_P ( PSTR ( " \" ToGroup \" :%d} " ) , group ) ;
} else if ( Z_Addr_IEEEAddress = = addrmode ) { // IEEE address mode
2021-01-24 15:35:36 +00:00
ResponseAppend_P ( PSTR ( " \" ToDevice \" : \" 0x%_X \" , \" ToEndpoint \" :%d} " ) , & dstaddr , dstep ) ;
2020-11-30 18:12:16 +00:00
}
2020-10-28 16:30:46 +00:00
}
2020-11-30 18:12:16 +00:00
ResponseAppend_P ( PSTR ( " ]}} " ) ) ;
}
2020-10-28 16:30:46 +00:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_MAP ) ) ;
2020-11-30 18:12:16 +00:00
// Check if there are more values waiting, if so re-send a new request to get other values
2020-12-13 18:18:36 +00:00
// Only send a new request if the current was non-empty. This avoids an infinite loop if the device announces more slots that it actually has.
if ( ( non_empty ) & & ( start + len < total ) ) {
2020-11-30 18:12:16 +00:00
// there are more values to read
# ifdef USE_ZIGBEE_ZNP
Z_Send_State_or_Map ( shortaddr , start + len , lqi ? ZDO_MGMT_LQI_REQ : ZDO_MGMT_BIND_REQ ) ;
# endif // USE_ZIGBEE_ZNP
# ifdef USE_ZIGBEE_EZSP
Z_Send_State_or_Map ( shortaddr , start + len , lqi ? ZDO_Mgmt_Lqi_req : ZDO_Mgmt_Bind_req ) ;
# endif // USE_ZIGBEE_EZSP
}
2020-10-28 16:30:46 +00:00
return - 1 ;
}
2020-11-30 18:12:16 +00:00
//
// Handle MgMt Bind Rsp incoming message
//
2021-01-25 15:02:56 +00:00
int32_t Z_MgmtLqiRsp ( int32_t res , const SBuffer & buf ) {
2020-11-30 18:12:16 +00:00
return Z_Mgmt_Lqi_Bind_Rsp ( res , buf , true ) ;
}
2020-07-26 12:54:24 +01:00
# ifdef USE_ZIGBEE_EZSP
2020-07-25 14:40:42 +01:00
//
// Handle Parent Annonce Rsp incoming message
//
// rsp: true = ZDO_Parent_annce_rsp, false = ZDO_Parent_annce
2021-01-25 15:02:56 +00:00
int32_t EZ_ParentAnnceRsp ( int32_t res , const SBuffer & buf , bool rsp ) {
2020-07-25 14:40:42 +01:00
size_t prefix_len ;
uint8_t status ;
uint8_t num_children ;
uint16_t shortaddr = buf . get16 ( buf . len ( ) - 2 ) ;
if ( rsp ) {
status = buf . get8 ( 0 ) ;
num_children = buf . get8 ( 1 ) ;
prefix_len = 2 ;
} else {
status = 0 ;
num_children = buf . get8 ( 0 ) ;
prefix_len = 1 ;
}
2020-11-22 15:07:09 +00:00
// device is reachable
zigbee_devices . deviceWasReached ( shortaddr ) ;
2020-07-25 14:40:42 +01:00
const char * friendlyName = zigbee_devices . getFriendlyName ( shortaddr ) ;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_PARENT " \" :{ \" " D_JSON_ZIGBEE_DEVICE " \" : \" 0x%04X \" " ) , shortaddr ) ;
if ( friendlyName ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_ZIGBEE_NAME " \" : \" %s \" " ) , friendlyName ) ;
}
if ( rsp ) {
ResponseAppend_P ( PSTR ( " , \" " D_JSON_ZIGBEE_STATUS " \" :%d "
" , \" " D_JSON_ZIGBEE_STATUS_MSG " \" : \" %s \" "
) , status , getZigbeeStatusMessage ( status ) . c_str ( ) ) ;
}
ResponseAppend_P ( PSTR ( " , \" Children \" :%d "
" , \" ChildInfo \" :[ "
) , num_children ) ;
uint32_t idx = prefix_len ;
for ( uint32_t i = 0 ; i < num_children ; i + + ) {
if ( idx + 8 > buf . len ( ) ) { break ; } // overflow, frame size is between 14 and 21
uint64_t child_ieee = buf . get64 ( idx ) ;
idx + = 8 ;
if ( i > 0 ) {
ResponseAppend_P ( PSTR ( " , " ) ) ;
}
2021-01-24 15:35:36 +00:00
ResponseAppend_P ( PSTR ( " \" 0x%_X \" " ) , & child_ieee ) ;
2020-07-25 14:40:42 +01:00
}
ResponseAppend_P ( PSTR ( " ]}} " ) ) ;
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_BIND_STATE ) ) ;
return - 1 ;
}
2020-07-26 12:54:24 +01:00
# endif // USE_ZIGBEE_EZSP
2020-07-25 14:40:42 +01:00
2020-03-23 21:46:26 +00:00
/*********************************************************************************************\
* Send specific ZNP messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//
// Send ZDO_IEEE_ADDR_REQ request to get IEEE long address
//
void Z_SendIEEEAddrReq ( uint16_t shortaddr ) {
2020-06-29 21:21:32 +01:00
# ifdef USE_ZIGBEE_ZNP
2020-03-23 21:46:26 +00:00
uint8_t IEEEAddrReq [ ] = { Z_SREQ | Z_ZDO , ZDO_IEEE_ADDR_REQ , Z_B0 ( shortaddr ) , Z_B1 ( shortaddr ) , 0x00 , 0x00 } ;
ZigbeeZNPSend ( IEEEAddrReq , sizeof ( IEEEAddrReq ) ) ;
2020-06-29 21:21:32 +01:00
# endif
2020-07-02 21:56:37 +01:00
# ifdef USE_ZIGBEE_EZSP
2020-07-05 20:01:26 +01:00
uint8_t IEEEAddrReq [ ] = { Z_B0 ( shortaddr ) , Z_B1 ( shortaddr ) , 0x00 , 0x00 } ;
EZ_SendZDO ( shortaddr , ZDO_IEEE_addr_req , IEEEAddrReq , sizeof ( IEEEAddrReq ) ) ;
2020-07-02 21:56:37 +01:00
# endif
2020-03-23 21:46:26 +00:00
}
//
// Send ACTIVE_EP_REQ to collect active endpoints for this address
//
void Z_SendActiveEpReq ( uint16_t shortaddr ) {
2020-06-29 21:21:32 +01:00
# ifdef USE_ZIGBEE_ZNP
2020-03-23 21:46:26 +00:00
uint8_t ActiveEpReq [ ] = { Z_SREQ | Z_ZDO , ZDO_ACTIVE_EP_REQ , Z_B0 ( shortaddr ) , Z_B1 ( shortaddr ) , Z_B0 ( shortaddr ) , Z_B1 ( shortaddr ) } ;
ZigbeeZNPSend ( ActiveEpReq , sizeof ( ActiveEpReq ) ) ;
2020-06-29 21:21:32 +01:00
# endif
# ifdef USE_ZIGBEE_EZSP
uint8_t ActiveEpReq [ ] = { Z_B0 ( shortaddr ) , Z_B1 ( shortaddr ) } ;
EZ_SendZDO ( shortaddr , ZDO_Active_EP_req , ActiveEpReq , sizeof ( ActiveEpReq ) ) ;
# endif
2020-03-23 21:46:26 +00:00
}
2020-09-12 09:57:54 +01:00
//
// Probe the clusters_out on the first endpoint
//
// Send ZDO_SIMPLE_DESC_REQ to get full list of supported Clusters for a specific endpoint
void Z_SendSimpleDescReq ( uint16_t shortaddr , uint16_t groupaddr , uint16_t cluster , uint8_t endpoint , uint32_t value ) {
# ifdef USE_ZIGBEE_ZNP
uint8_t SimpleDescReq [ ] = { Z_SREQ | Z_ZDO , ZDO_SIMPLE_DESC_REQ , // 2504
Z_B0 ( shortaddr ) , Z_B1 ( shortaddr ) , Z_B0 ( shortaddr ) , Z_B1 ( shortaddr ) ,
endpoint } ;
ZigbeeZNPSend ( SimpleDescReq , sizeof ( SimpleDescReq ) ) ;
# endif
# ifdef USE_ZIGBEE_EZSP
uint8_t SimpleDescReq [ ] = { Z_B0 ( shortaddr ) , Z_B1 ( shortaddr ) , endpoint } ;
EZ_SendZDO ( shortaddr , ZDO_SIMPLE_DESC_REQ , SimpleDescReq , sizeof ( SimpleDescReq ) ) ;
# endif
}
2020-03-23 21:46:26 +00:00
//
// Send AF Info Request
2020-09-12 09:57:54 +01:00
// Queue requests for the device
2021-02-02 19:46:18 +00:00
// 1. Request for 'ModelId' and 'Manufacturer': 0000/0005, 0000/0004
2020-09-12 09:57:54 +01:00
// 2. Auto-bind to coordinator:
2020-10-30 11:29:48 +00:00
// Iterate among
2020-03-23 21:46:26 +00:00
//
2020-09-12 09:57:54 +01:00
void Z_SendDeviceInfoRequest ( uint16_t shortaddr ) {
2022-05-16 18:17:40 +01:00
ZCLFrame zcl ( 4 ) ; // message is 4 bytes
2021-02-02 19:46:18 +00:00
zcl . shortaddr = shortaddr ;
zcl . cluster = 0 ;
zcl . cmd = ZCL_READ_ATTRIBUTES ;
zcl . clusterSpecific = false ;
zcl . needResponse = true ;
zcl . direct = false ; // discover route
2022-05-16 18:17:40 +01:00
zcl . payload . add16 ( 0x0005 ) ;
zcl . payload . add16 ( 0x0004 ) ;
2021-02-02 19:46:18 +00:00
zigbeeZCLSendCmd ( zcl ) ;
2020-03-23 21:46:26 +00:00
}
2020-09-12 09:57:54 +01:00
//
2020-12-21 11:13:57 +00:00
// Send single attribute read request in Timer
2020-09-12 09:57:54 +01:00
//
void Z_SendSingleAttributeRead ( uint16_t shortaddr , uint16_t groupaddr , uint16_t cluster , uint8_t endpoint , uint32_t value ) {
2022-05-16 18:17:40 +01:00
ZCLFrame zcl ( 2 ) ; // message is 2 bytes
2021-02-02 19:46:18 +00:00
zcl . shortaddr = shortaddr ;
zcl . cluster = cluster ;
2022-05-16 18:17:40 +01:00
zcl . dstendpoint = endpoint ;
2021-02-02 19:46:18 +00:00
zcl . cmd = ZCL_READ_ATTRIBUTES ;
zcl . clusterSpecific = false ;
zcl . needResponse = true ;
zcl . direct = false ; // discover route
2022-05-16 18:17:40 +01:00
zcl . payload . add16 ( value ) ; // 04000500
2021-02-02 19:46:18 +00:00
zigbeeZCLSendCmd ( zcl ) ;
2020-09-12 09:57:54 +01:00
}
2020-12-21 11:13:57 +00:00
//
// Write CIE address
//
void Z_WriteCIEAddress ( uint16_t shortaddr , uint16_t groupaddr , uint16_t cluster , uint8_t endpoint , uint32_t value ) {
2022-04-20 15:06:13 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_ZIGBEE " Sending CIE Address for Cluster %d in Endpoint %d of Device 0x%04X " ) , cluster , endpoint , shortaddr ) ;
2022-05-16 18:17:40 +01:00
ZCLFrame zcl ( 12 ) ; // message is 12 bytes
2021-02-02 19:46:18 +00:00
zcl . shortaddr = shortaddr ;
zcl . cluster = 0x0500 ;
2022-05-16 18:17:40 +01:00
zcl . dstendpoint = endpoint ;
2021-02-02 19:46:18 +00:00
zcl . cmd = ZCL_WRITE_ATTRIBUTES ;
zcl . clusterSpecific = false ;
zcl . needResponse = true ;
zcl . direct = false ; // discover route
2022-05-16 18:17:40 +01:00
zcl . payload . add16 ( 0x0010 ) ; // attribute 0x0010
zcl . payload . add8 ( ZEUI64 ) ;
zcl . payload . add64 ( localIEEEAddr ) ;
2021-02-02 19:46:18 +00:00
zigbeeZCLSendCmd ( zcl ) ;
2020-12-21 11:13:57 +00:00
}
//
2022-04-20 15:06:13 +01:00
// Send CIE Zone Enroll Response
2020-12-21 11:13:57 +00:00
//
void Z_SendCIEZoneEnrollResponse ( uint16_t shortaddr , uint16_t groupaddr , uint16_t cluster , uint8_t endpoint , uint32_t value ) {
2022-04-20 15:06:13 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_ZIGBEE " Sending Enroll Zone %d for Cluster %d in Endpoint %d of Device 0x%04X " ) , Z_B0 ( value ) , cluster , endpoint , shortaddr ) ;
2022-05-16 18:17:40 +01:00
ZCLFrame zcl ( 2 ) ; // message is 2 bytes
2021-02-02 19:46:18 +00:00
zcl . shortaddr = shortaddr ;
zcl . cluster = 0x0500 ;
2022-05-16 18:17:40 +01:00
zcl . dstendpoint = endpoint ;
2022-04-20 15:06:13 +01:00
zcl . cmd = 0x00 ; // Zone Enroll Response
2021-02-02 19:46:18 +00:00
zcl . clusterSpecific = true ;
zcl . needResponse = true ;
zcl . direct = false ; // discover route
2022-05-16 18:17:40 +01:00
zcl . payload . add8 ( 0x00 ) ; // success
zcl . payload . add8 ( Z_B0 ( value ) ) ; // ZoneID
2021-02-02 19:46:18 +00:00
zigbeeZCLSendCmd ( zcl ) ;
2020-12-21 11:13:57 +00:00
}
2020-09-12 09:57:54 +01:00
//
// Auto-bind some clusters to the coordinator's endpoint 0x01
//
void Z_AutoBind ( uint16_t shortaddr , uint16_t groupaddr , uint16_t cluster , uint8_t endpoint , uint32_t value ) {
uint64_t srcLongAddr = zigbee_devices . getDeviceLongAddr ( shortaddr ) ;
2020-10-30 11:29:48 +00:00
2021-06-05 10:47:09 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_ZIGBEE " auto-bind `ZbBind { \" Device \" : \" 0x%04X \" , \" Endpoint \" :%d, \" Cluster \" : \" 0x%04X \" }` " ) ,
2020-09-12 09:57:54 +01:00
shortaddr , endpoint , cluster ) ;
# ifdef USE_ZIGBEE_ZNP
SBuffer buf ( 34 ) ;
buf . add8 ( Z_SREQ | Z_ZDO ) ;
buf . add8 ( ZDO_BIND_REQ ) ;
buf . add16 ( shortaddr ) ;
buf . add64 ( srcLongAddr ) ;
buf . add8 ( endpoint ) ;
buf . add16 ( cluster ) ;
buf . add8 ( Z_Addr_IEEEAddress ) ; // DstAddrMode - 0x03 = ADDRESS_64_BIT
buf . add64 ( localIEEEAddr ) ;
buf . add8 ( 0x01 ) ; // toEndpoint
ZigbeeZNPSend ( buf . getBuffer ( ) , buf . len ( ) ) ;
# endif // USE_ZIGBEE_ZNP
# ifdef USE_ZIGBEE_EZSP
SBuffer buf ( 24 ) ;
// ZDO message payload (see Zigbee spec 2.4.3.2.2)
buf . add64 ( srcLongAddr ) ;
buf . add8 ( endpoint ) ;
buf . add16 ( cluster ) ;
buf . add8 ( Z_Addr_IEEEAddress ) ; // DstAddrMode - 0x03 = ADDRESS_64_BIT
buf . add64 ( localIEEEAddr ) ;
buf . add8 ( 0x01 ) ; // toEndpoint
EZ_SendZDO ( shortaddr , ZDO_BIND_REQ , buf . buf ( ) , buf . len ( ) ) ;
# endif // USE_ZIGBEE_EZSP
}
//
// Auto-bind some clusters to the coordinator's endpoint 0x01
//
// the structure below indicates which attributes need to be configured for attribute reporting
typedef struct Z_autoAttributeReporting_t {
uint16_t cluster ;
uint16_t attr_id ;
uint16_t min_interval ; // minimum interval in seconds (consecutive reports won't happen before this value)
uint16_t max_interval ; // maximum interval in seconds (attribut will always be reported after this interval)
float report_change ; // for non discrete attributes, the value change that triggers a report
} Z_autoAttributeReporting_t ;
// Note the attribute must be registered in the converter list, used to retrieve the type of the attribute
const Z_autoAttributeReporting_t Z_autoAttributeReporting [ ] PROGMEM = {
2021-01-10 15:04:12 +00:00
{ 0x0001 , 0x0020 , 60 * 60 , USE_ZIGBEE_MAXTIME_BATT , USE_ZIGBEE_AUTOBIND_BATTVOLTAGE } , // BatteryVoltage
{ 0x0001 , 0x0021 , 60 * 60 , USE_ZIGBEE_MAXTIME_BATT , USE_ZIGBEE_AUTOBIND_BATTPERCENT } , // BatteryPercentage
{ 0x0006 , 0x0000 , 1 , USE_ZIGBEE_MAXTIME_LIGHT , 0 } , // Power
2021-02-18 15:27:03 +00:00
{ 0x0102 , 0x0008 , 1 , USE_ZIGBEE_MAXTIME_LIFT , USE_ZIGBEE_AUTOBIND_LIFT } , // CurrentPositionLiftPercentage
2021-01-10 15:04:12 +00:00
{ 0x0201 , 0x0000 , 60 , USE_ZIGBEE_MAXTIME_TRV , USE_ZIGBEE_AUTOBIND_TEMPERATURE } , // LocalTemperature
{ 0x0201 , 0x0008 , 60 , USE_ZIGBEE_MAXTIME_TRV , USE_ZIGBEE_AUTOBIND_HEATDEMAND } , // PIHeatingDemand
{ 0x0201 , 0x0012 , 60 , USE_ZIGBEE_MAXTIME_TRV , USE_ZIGBEE_AUTOBIND_TEMPERATURE } , // OccupiedHeatingSetpoint
{ 0x0008 , 0x0000 , 1 , USE_ZIGBEE_MAXTIME_LIGHT , 5 } , // Dimmer
{ 0x0300 , 0x0000 , 1 , USE_ZIGBEE_MAXTIME_LIGHT , 5 } , // Hue
{ 0x0300 , 0x0001 , 1 , USE_ZIGBEE_MAXTIME_LIGHT , 5 } , // Sat
{ 0x0300 , 0x0003 , 1 , USE_ZIGBEE_MAXTIME_LIGHT , 100 } , // X
{ 0x0300 , 0x0004 , 1 , USE_ZIGBEE_MAXTIME_LIGHT , 100 } , // Y
{ 0x0300 , 0x0007 , 1 , USE_ZIGBEE_MAXTIME_LIGHT , 5 } , // CT
{ 0x0300 , 0x0008 , 1 , USE_ZIGBEE_MAXTIME_LIGHT , 0 } , // ColorMode
{ 0x0400 , 0x0000 , 10 , USE_ZIGBEE_MAXTIME_SENSOR , USE_ZIGBEE_AUTOBIND_ILLUMINANCE } , // Illuminance (5 lux)
{ 0x0402 , 0x0000 , 30 , USE_ZIGBEE_MAXTIME_SENSOR , USE_ZIGBEE_AUTOBIND_TEMPERATURE } , // Temperature (0.2 °C)
{ 0x0403 , 0x0000 , 30 , USE_ZIGBEE_MAXTIME_SENSOR , USE_ZIGBEE_AUTOBIND_PRESSURE } , // Pressure (1 hPa)
{ 0x0405 , 0x0000 , 30 , USE_ZIGBEE_MAXTIME_SENSOR , USE_ZIGBEE_AUTOBIND_HUMIDITY } , // Humidity (1 %)
{ 0x0406 , 0x0000 , 10 , USE_ZIGBEE_MAXTIME_SENSOR , 0 } , // Occupancy
{ 0x0500 , 0x0002 , 1 , USE_ZIGBEE_MAXTIME_SENSOR , 0 } , // ZoneStatus
2020-09-12 09:57:54 +01:00
} ;
//
// Called by Device Auto-config
// Configures default values for the most common Attribute Rerporting configurations
//
// Note: must be of type `Z_DeviceTimer`
void Z_AutoConfigReportingForCluster ( uint16_t shortaddr , uint16_t groupaddr , uint16_t cluster , uint8_t endpoint , uint32_t value ) {
// Buffer, max 12 bytes per attribute
SBuffer buf ( 12 * 6 ) ;
Response_P ( PSTR ( " ZbSend { \" Device \" : \" 0x%04X \" , \" Config \" :{ " ) , shortaddr ) ;
boolean comma = false ;
2021-02-28 11:50:02 +00:00
for ( uint32_t i = 0 ; i < nitems ( Z_autoAttributeReporting ) ; i + + ) {
2020-09-12 09:57:54 +01:00
uint16_t conv_cluster = pgm_read_word ( & ( Z_autoAttributeReporting [ i ] . cluster ) ) ;
uint16_t attr_id = pgm_read_word ( & ( Z_autoAttributeReporting [ i ] . attr_id ) ) ;
if ( conv_cluster = = cluster ) {
uint16_t min_interval = pgm_read_word ( & ( Z_autoAttributeReporting [ i ] . min_interval ) ) ;
uint16_t max_interval = pgm_read_word ( & ( Z_autoAttributeReporting [ i ] . max_interval ) ) ;
float report_change_raw = Z_autoAttributeReporting [ i ] . report_change ;
double report_change = report_change_raw ;
uint8_t attr_type ;
int8_t multiplier ;
const __FlashStringHelper * attr_name = zigbeeFindAttributeById ( cluster , attr_id , & attr_type , & multiplier ) ;
if ( attr_name ) {
if ( comma ) { ResponseAppend_P ( PSTR ( " , " ) ) ; }
comma = true ;
ResponseAppend_P ( PSTR ( " \" %s \" :{ \" MinInterval \" :%d, \" MaxInterval \" :%d " ) , attr_name , min_interval , max_interval ) ;
buf . add8 ( 0 ) ; // direction, always 0
buf . add16 ( attr_id ) ;
buf . add8 ( attr_type ) ;
buf . add16 ( min_interval ) ;
buf . add16 ( max_interval ) ;
if ( ! Z_isDiscreteDataType ( attr_type ) ) { // report_change is only valid for non-discrete data types (numbers)
ZbApplyMultiplier ( report_change , multiplier ) ;
// encode value
int32_t res = encodeSingleAttribute ( buf , report_change , " " , attr_type ) ;
if ( res < 0 ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_ERROR , PSTR ( D_LOG_ZIGBEE " internal error, unsupported attribute type " ) ) ;
2020-09-12 09:57:54 +01:00
} else {
Z_attribute attr ;
attr . setKeyName ( PSTR ( " ReportableChange " ) , true ) ; // true because in PMEM
attr . setFloat ( report_change_raw ) ;
ResponseAppend_P ( PSTR ( " ,%s " ) , attr . toString ( ) . c_str ( ) ) ;
}
}
ResponseAppend_P ( PSTR ( " } " ) ) ;
}
}
}
ResponseAppend_P ( PSTR ( " }} " ) ) ;
if ( buf . len ( ) > 0 ) {
2021-08-15 16:08:31 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( D_LOG_ZIGBEE " auto-bind `%s` " ) , ResponseData ( ) ) ;
2022-05-16 18:17:40 +01:00
ZCLFrame zcl ( buf . len ( ) ) ; // message is 4 bytes
2021-02-02 19:46:18 +00:00
zcl . shortaddr = shortaddr ;
zcl . cluster = cluster ;
2022-05-16 18:17:40 +01:00
zcl . dstendpoint = endpoint ;
2021-02-02 19:46:18 +00:00
zcl . cmd = ZCL_CONFIGURE_REPORTING ;
zcl . clusterSpecific = false ; /* not cluster specific */
zcl . needResponse = false ; /* noresponse */
zcl . direct = false ; /* discover route */
2022-05-16 18:17:40 +01:00
zcl . payload . addBuffer ( buf ) ;
2021-02-02 19:46:18 +00:00
zigbeeZCLSendCmd ( zcl ) ;
2020-09-12 09:57:54 +01:00
}
}
2020-06-16 19:01:14 +01:00
//
2020-06-29 21:21:32 +01:00
// Handle trustCenterJoinHandler
// 2400
2020-06-16 19:01:14 +01:00
//
2020-06-29 21:21:32 +01:00
# ifdef USE_ZIGBEE_EZSP
2021-01-25 15:02:56 +00:00
int32_t EZ_ReceiveTCJoinHandler ( int32_t res , const SBuffer & buf ) {
2020-06-29 21:21:32 +01:00
uint16_t srcAddr = buf . get16 ( 2 ) ;
uint64_t ieeeAddr = buf . get64 ( 4 ) ;
uint8_t status = buf . get8 ( 12 ) ;
uint8_t decision = buf . get8 ( 13 ) ;
uint16_t parentNw = buf . get16 ( 14 ) ;
2020-03-23 21:46:26 +00:00
2020-06-29 21:21:32 +01:00
if ( EMBER_DEVICE_LEFT ! = status ) { // ignore message if the device is leaving
zigbee_devices . updateDevice ( srcAddr , ieeeAddr ) ;
2020-03-23 21:46:26 +00:00
2020-06-29 21:21:32 +01:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2021-01-24 15:35:36 +00:00
" \" Status \" :%d, \" IEEEAddr \" : \" 0x%_X \" , \" ShortAddr \" : \" 0x%04X \" "
2020-06-29 21:21:32 +01:00
" , \" ParentNetwork \" : \" 0x%04X \" "
2020-12-20 21:50:10 +00:00
" , \" JoinStatus \" :%d, \" Decision \" :%d "
2020-06-29 21:21:32 +01:00
" }} " ) ,
2021-01-24 15:35:36 +00:00
ZIGBEE_STATUS_DEVICE_INDICATION , & ieeeAddr , srcAddr , parentNw ,
2020-06-29 21:21:32 +01:00
status , decision
) ;
2019-12-22 16:47:45 +00:00
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
2019-12-22 16:47:45 +00:00
}
2020-06-29 21:21:32 +01:00
return - 1 ;
2019-12-22 16:47:45 +00:00
}
2020-06-29 21:21:32 +01:00
# endif // USE_ZIGBEE_EZSP
2019-12-22 16:47:45 +00:00
2020-06-29 21:21:32 +01:00
//
2020-07-02 21:56:37 +01:00
// Parse incoming ZCL message.
2020-06-29 21:21:32 +01:00
//
2020-07-02 21:56:37 +01:00
// This code is common to ZNP and EZSP
2020-09-05 13:44:31 +01:00
void Z_IncomingMessage ( class ZCLFrame & zcl_received ) {
2020-06-29 21:21:32 +01:00
uint16_t srcaddr = zcl_received . getSrcAddr ( ) ;
uint16_t groupid = zcl_received . getGroupAddr ( ) ;
uint16_t clusterid = zcl_received . getClusterId ( ) ;
uint8_t linkquality = zcl_received . getLinkQuality ( ) ;
uint8_t srcendpoint = zcl_received . getSrcEndpoint ( ) ;
2020-09-05 13:44:31 +01:00
linkquality = linkquality ! = 0xFF ? linkquality : 0xFE ; // avoid 0xFF (reserved for unknown)
2019-09-29 14:38:26 +01:00
2019-12-22 16:47:45 +00:00
bool defer_attributes = false ; // do we defer attributes reporting to coalesce
2020-06-29 21:21:32 +01:00
// log the packet details
2019-12-15 18:39:27 +00:00
zcl_received . log ( ) ;
2020-06-27 17:17:40 +01:00
2022-05-16 18:17:40 +01:00
# ifdef USE_BERRY
// Berry pre-process messages
2022-05-24 21:45:04 +01:00
callBerryZigbeeDispatcher ( " incoming " , " zcl_frame " , & zcl_received , 0 ) ;
2022-05-16 18:17:40 +01:00
# endif // USE_BERRY
2020-11-06 20:59:08 +00:00
// create the device entry if it does not exist and if it's not the local device
Z_Device & device = ( srcaddr ! = localShortAddr ) ? zigbee_devices . getShortAddr ( srcaddr ) :
device_unk ;
if ( device . valid ( ) ) {
2020-11-01 18:00:07 +00:00
device . setLQI ( linkquality ! = 0xFF ? linkquality : 0xFE ) ; // EFR32 has a different scale for LQI
device . setLastSeenNow ( ) ;
2020-11-22 15:07:09 +00:00
zigbee_devices . deviceWasReached ( srcaddr ) ;
2020-11-01 18:00:07 +00:00
}
2020-06-27 17:17:40 +01:00
2019-09-29 14:38:26 +01:00
char shortaddr [ 8 ] ;
snprintf_P ( shortaddr , sizeof ( shortaddr ) , PSTR ( " 0x%04X " ) , srcaddr ) ;
2020-07-05 11:35:54 +01:00
2020-09-05 13:44:31 +01:00
Z_attribute_list attr_list ;
attr_list . lqi = linkquality ;
attr_list . src_ep = srcendpoint ;
if ( groupid ) { // TODO we miss the group_id == 0 here
attr_list . group_id = groupid ;
}
2020-06-27 17:17:40 +01:00
2020-03-01 10:25:59 +00:00
if ( ( ! zcl_received . isClusterSpecificCommand ( ) ) & & ( ZCL_DEFAULT_RESPONSE = = zcl_received . getCmdId ( ) ) ) {
2020-09-05 13:44:31 +01:00
zcl_received . parseResponse ( ) ; // Zigbee general "Default Response", publish ZbResponse message
2020-06-27 17:17:40 +01:00
} else {
2020-09-05 13:44:31 +01:00
// Build the ZbReceive list
2021-01-10 10:47:12 +00:00
if ( ( ! zcl_received . isClusterSpecificCommand ( ) ) & & ( ZCL_REPORT_ATTRIBUTES = = zcl_received . getCmdId ( ) | | ZCL_WRITE_ATTRIBUTES = = zcl_received . getCmdId ( ) ) ) {
2020-09-05 13:44:31 +01:00
zcl_received . parseReportAttributes ( attr_list ) ; // Zigbee report attributes from sensors
2021-01-10 10:47:12 +00:00
if ( clusterid & & ( ZCL_REPORT_ATTRIBUTES = = zcl_received . getCmdId ( ) ) ) { defer_attributes = true ; } // don't defer system Cluster=0 messages or Write Attribute
2020-03-01 10:25:59 +00:00
} else if ( ( ! zcl_received . isClusterSpecificCommand ( ) ) & & ( ZCL_READ_ATTRIBUTES_RESPONSE = = zcl_received . getCmdId ( ) ) ) {
2020-09-05 13:44:31 +01:00
zcl_received . parseReadAttributesResponse ( attr_list ) ;
2020-05-29 21:52:45 +01:00
if ( clusterid ) { defer_attributes = true ; } // don't defer system Cluster=0 messages
} else if ( ( ! zcl_received . isClusterSpecificCommand ( ) ) & & ( ZCL_READ_ATTRIBUTES = = zcl_received . getCmdId ( ) ) ) {
2020-09-05 13:44:31 +01:00
zcl_received . parseReadAttributes ( attr_list ) ;
2020-06-03 21:39:04 +01:00
// never defer read_attributes, so the auto-responder can send response back on a per cluster basis
2020-08-08 11:17:37 +01:00
} else if ( ( ! zcl_received . isClusterSpecificCommand ( ) ) & & ( ZCL_READ_REPORTING_CONFIGURATION_RESPONSE = = zcl_received . getCmdId ( ) ) ) {
2020-09-05 13:44:31 +01:00
zcl_received . parseReadConfigAttributes ( attr_list ) ;
2020-08-08 11:17:37 +01:00
} else if ( ( ! zcl_received . isClusterSpecificCommand ( ) ) & & ( ZCL_CONFIGURE_REPORTING_RESPONSE = = zcl_received . getCmdId ( ) ) ) {
2020-09-05 13:44:31 +01:00
zcl_received . parseConfigAttributes ( attr_list ) ;
2020-12-21 11:13:57 +00:00
} else if ( ( ! zcl_received . isClusterSpecificCommand ( ) ) & & ( ZCL_WRITE_ATTRIBUTES_RESPONSE = = zcl_received . getCmdId ( ) ) ) {
zcl_received . parseWriteAttributesResponse ( attr_list ) ;
2020-03-01 10:25:59 +00:00
} else if ( zcl_received . isClusterSpecificCommand ( ) ) {
2020-09-05 13:44:31 +01:00
zcl_received . parseClusterSpecificCommand ( attr_list ) ;
2020-03-01 10:25:59 +00:00
}
2020-06-03 21:39:04 +01:00
2021-06-05 10:47:09 +01:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED " : { \" 0x%04X \" :{%s}} " ) , srcaddr , attr_list . toString ( ) . c_str ( ) ) ;
2020-03-01 10:25:59 +00:00
2020-07-10 19:15:12 +01:00
// discard the message if it was sent by us (broadcast or group loopback)
if ( srcaddr = = localShortAddr ) {
2021-01-23 15:26:23 +00:00
AddLog ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_ZIGBEE " loopback message, ignoring " ) ) ;
2020-07-10 19:15:12 +01:00
return ; // abort the rest of message management
}
2020-09-05 13:44:31 +01:00
zcl_received . generateSyntheticAttributes ( attr_list ) ;
2020-11-27 21:53:42 +00:00
zcl_received . removeInvalidAttributes ( attr_list ) ;
2020-09-27 15:50:48 +01:00
zcl_received . computeSyntheticAttributes ( attr_list ) ;
2020-09-05 13:44:31 +01:00
zcl_received . generateCallBacks ( attr_list ) ; // set deferred callbacks, ex: Occupancy
2020-10-28 19:59:55 +00:00
Z_postProcessAttributes ( srcaddr , zcl_received . getSrcEndpoint ( ) , attr_list ) ;
2020-03-01 10:25:59 +00:00
if ( defer_attributes ) {
// Prepare for publish
2020-09-05 13:44:31 +01:00
if ( zigbee_devices . jsonIsConflict ( srcaddr , attr_list ) ) {
2020-03-01 10:25:59 +00:00
// there is conflicting values, force a publish of the previous message now and don't coalesce
zigbee_devices . jsonPublishFlush ( srcaddr ) ;
}
2020-09-05 13:44:31 +01:00
zigbee_devices . jsonAppend ( srcaddr , attr_list ) ;
2020-03-14 13:17:30 +00:00
zigbee_devices . setTimer ( srcaddr , 0 /* groupaddr */ , USE_ZIGBEE_COALESCE_ATTR_TIMER , clusterid , srcendpoint , Z_CAT_READ_ATTR , 0 , & Z_PublishAttributes ) ;
2020-03-01 10:25:59 +00:00
} else {
// Publish immediately
2020-09-05 13:44:31 +01:00
zigbee_devices . jsonPublishNow ( srcaddr , attr_list ) ;
2019-12-23 15:53:54 +00:00
}
2019-12-22 16:47:45 +00:00
}
2020-06-29 21:21:32 +01:00
}
# ifdef USE_ZIGBEE_EZSP
/*********************************************************************************************\
* Send ZDO Message
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-11-30 18:12:16 +00:00
void EZ_SendZDO ( uint16_t shortaddr , uint16_t cmd , const unsigned char * payload , size_t payload_len , bool retry ) {
2020-07-02 21:56:37 +01:00
SBuffer buf ( payload_len + 22 ) ;
2020-06-29 21:21:32 +01:00
uint8_t seq = zigbee_devices . getNextSeqNumber ( 0x0000 ) ;
2020-07-20 18:30:32 +01:00
if ( shortaddr < 0xFFFC ) {
// send unicast
buf . add16 ( EZSP_sendUnicast ) ;
buf . add8 ( EMBER_OUTGOING_DIRECT ) ; // 00
buf . add16 ( shortaddr ) ; // dest addr
// ApsFrame
buf . add16 ( 0x0000 ) ; // ZOD profile
buf . add16 ( cmd ) ; // ZDO cmd in cluster
buf . add8 ( 0 ) ; // srcEp
buf . add8 ( 0 ) ; // dstEp
2020-11-30 18:12:16 +00:00
if ( retry ) {
buf . add16 ( EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY ) ; // APS frame
} else {
buf . add16 ( EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY ) ; // APS frame
}
2020-07-20 18:30:32 +01:00
buf . add16 ( 0x0000 ) ; // groupId
buf . add8 ( seq ) ;
// end of ApsFrame
buf . add8 ( 0x01 ) ; // tag TODO
buf . add8 ( payload_len + 1 ) ; // insert seq number
buf . add8 ( seq ) ;
buf . addBuffer ( payload , payload_len ) ;
} else {
// send broadcast
buf . add16 ( EZSP_sendBroadcast ) ;
buf . add16 ( shortaddr ) ; // dest addr
// ApsFrame
buf . add16 ( 0x0000 ) ; // ZOD profile
buf . add16 ( cmd ) ; // ZDO cmd in cluster
buf . add8 ( 0 ) ; // srcEp
buf . add8 ( 0 ) ; // dstEp
buf . add16 ( 0x00 ) ; // APS frame
buf . add16 ( 0x0000 ) ; // groupId
buf . add8 ( seq ) ;
// end of ApsFrame
buf . add8 ( 0x1E ) ; // radius
buf . add8 ( 0x01 ) ; // tag TODO
buf . add8 ( payload_len + 1 ) ; // insert seq number
buf . add8 ( seq ) ;
buf . addBuffer ( payload , payload_len ) ;
}
2020-07-02 21:56:37 +01:00
2020-07-22 18:29:16 +01:00
ZigbeeEZSPSendCmd ( buf . buf ( ) , buf . len ( ) ) ;
2020-06-29 21:21:32 +01:00
}
/*********************************************************************************************\
* Send specific EZSP messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-01-25 15:02:56 +00:00
int32_t EZ_IncomingMessage ( int32_t res , const SBuffer & buf ) {
2020-06-29 21:21:32 +01:00
uint8_t msgtype = buf . get8 ( 2 ) ; // see EZSP_EmberIncomingMessageType
bool wasbroadcast = ( msgtype > = EMBER_INCOMING_MULTICAST ) & & ( msgtype < = EMBER_INCOMING_BROADCAST_LOOPBACK ) ;
uint16_t profileid = buf . get16 ( 3 ) ; // HA = 0x0104, ZDO = 0x0000
uint16_t clusterid = buf . get16 ( 5 ) ;
uint8_t srcendpoint = buf . get8 ( 7 ) ;
uint8_t dstendpoint = buf . get8 ( 8 ) ;
uint16_t apsoptions = buf . get16 ( 9 ) ; // see EZSP_EmberApsOption, usually EMBER_APS_OPTION_ENABLE_ADDRESS_DISCOVERY
bool securityuse = ( apsoptions & EMBER_APS_OPTION_ENCRYPTION ) ? true : false ;
uint16_t groupid = buf . get16 ( 11 ) ;
uint8_t seqnumber = buf . get8 ( 13 ) ;
2020-07-29 09:02:04 +01:00
int8_t linkrssi = buf . get8 ( 15 ) ;
2020-10-30 11:29:48 +00:00
uint8_t linkquality = ZNP_RSSI2Lqi ( linkrssi ) ; // don't take EZSP LQI but calculate our own based on ZNP
2020-06-29 21:21:32 +01:00
uint16_t srcaddr = buf . get16 ( 16 ) ;
2020-07-05 20:01:26 +01:00
// uint8_t bindingindex = buf.get8(18); // not sure we need this one as a coordinator
// uint8_t addressindex = buf.get8(19); // not sure how to handle this one
2020-06-29 21:21:32 +01:00
// offset 20 is len, and buffer starts at offset 21
if ( ( 0x0000 = = profileid ) & & ( 0x00 = = srcendpoint ) ) {
// ZDO request
2020-08-09 09:44:32 +01:00
// Report LQI
2020-11-01 18:00:07 +00:00
if ( srcaddr ! = localShortAddr ) {
2020-11-02 17:29:04 +00:00
Z_Device & device = zigbee_devices . getShortAddr ( srcaddr ) ;
2020-11-01 18:00:07 +00:00
device . setLQI ( linkquality ) ;
device . setLastSeenNow ( ) ;
}
2020-07-02 21:56:37 +01:00
// Since ZDO messages start with a sequence number, we skip it
2020-07-05 20:01:26 +01:00
// but we add the source address in the last 2 bytes
SBuffer zdo_buf ( buf . get8 ( 20 ) - 1 + 2 ) ;
zdo_buf . addBuffer ( buf . buf ( 22 ) , buf . get8 ( 20 ) - 1 ) ;
zdo_buf . add16 ( srcaddr ) ;
2020-06-29 21:21:32 +01:00
switch ( clusterid ) {
case ZDO_Device_annce :
2020-07-02 21:56:37 +01:00
return Z_ReceiveEndDeviceAnnonce ( res , zdo_buf ) ;
case ZDO_Active_EP_rsp :
return Z_ReceiveActiveEp ( res , zdo_buf ) ;
case ZDO_IEEE_addr_rsp :
return Z_ReceiveIEEEAddr ( res , zdo_buf ) ;
2020-09-12 09:57:54 +01:00
case ZDO_Simple_Desc_rsp :
return Z_ReceiveSimpleDesc ( res , zdo_buf ) ;
2020-07-05 20:01:26 +01:00
case ZDO_Bind_rsp :
return Z_BindRsp ( res , zdo_buf ) ;
case ZDO_Unbind_rsp :
return Z_UnbindRsp ( res , zdo_buf ) ;
2020-10-28 16:30:46 +00:00
case ZDO_Mgmt_Lqi_rsp :
return Z_MgmtLqiRsp ( res , zdo_buf ) ;
2020-07-05 20:01:26 +01:00
case ZDO_Mgmt_Bind_rsp :
return Z_MgmtBindRsp ( res , zdo_buf ) ;
2020-07-25 14:40:42 +01:00
case ZDO_Parent_annce :
2020-07-26 12:54:24 +01:00
return EZ_ParentAnnceRsp ( res , zdo_buf , false ) ;
2020-07-25 14:40:42 +01:00
case ZDO_Parent_annce_rsp :
2020-07-26 12:54:24 +01:00
return EZ_ParentAnnceRsp ( res , zdo_buf , true ) ;
2020-07-25 14:40:42 +01:00
default :
break ;
2020-06-29 21:21:32 +01:00
}
} else {
ZCLFrame zcl_received = ZCLFrame : : parseRawFrame ( buf , 21 , buf . get8 ( 20 ) , clusterid , groupid ,
srcaddr ,
srcendpoint , dstendpoint , wasbroadcast ,
linkquality , securityuse , seqnumber ) ;
//
Z_IncomingMessage ( zcl_received ) ;
}
return - 1 ;
}
//
2020-07-21 21:39:39 +01:00
// Callback for resetting the NCP, called by the state machine
2020-06-29 21:21:32 +01:00
//
2020-07-04 18:59:59 +01:00
// value = 0 : drive reset pin and halt MCU
// value = 1 : release the reset pin, restart
2020-07-02 21:56:37 +01:00
int32_t EZ_Reset_Device ( uint8_t value ) {
2020-07-22 09:12:24 +01:00
/*
2020-07-04 18:59:59 +01:00
// we use Led4i to drive the reset pin. Since it is reverted we need to pass 1 to start reset, and 0 to release reset
2020-07-05 11:35:54 +01:00
if ( PinUsed ( GPIO_LED1 , ZIGBEE_EZSP_RESET_LED - 1 ) ) {
2020-07-04 18:59:59 +01:00
SetLedPowerIdx ( ZIGBEE_EZSP_RESET_LED - 1 , value ? 0 : 1 ) ;
2020-07-22 09:12:24 +01:00
*/
if ( PinUsed ( GPIO_ZIGBEE_RST ) ) {
digitalWrite ( Pin ( GPIO_ZIGBEE_RST ) , value ) ;
2020-07-04 18:59:59 +01:00
} else {
// no GPIO so we use software Reset instead
if ( value ) { // send reset only when we are supposed to release reset
uint8_t ezsp_reset [ 1 ] = { 0xC0 } ; // EZSP ASH Reset
ZigbeeEZSPSendRaw ( ezsp_reset , sizeof ( ezsp_reset ) , true ) ;
}
}
2020-06-29 21:21:32 +01:00
return 0 ; // continue
}
/*********************************************************************************************\
* Default resolver
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-01-25 15:02:56 +00:00
int32_t EZ_Recv_Default ( int32_t res , const SBuffer & buf ) {
2020-06-29 21:21:32 +01:00
// Default message handler for new messages
if ( zigbee . init_phase ) {
// if still during initialization phase, ignore any unexpected message
return - 1 ; // ignore message
} else {
2020-07-02 21:56:37 +01:00
uint16_t ezsp_command_index = buf . get16 ( 0 ) ;
switch ( ezsp_command_index ) {
2020-06-29 21:21:32 +01:00
case EZSP_incomingMessageHandler :
return EZ_IncomingMessage ( res , buf ) ;
case EZSP_trustCenterJoinHandler :
return EZ_ReceiveTCJoinHandler ( res , buf ) ;
2020-07-25 14:40:42 +01:00
case EZSP_incomingRouteErrorHandler :
return EZ_RouteError ( res , buf ) ;
case EZSP_permitJoining :
return EZ_PermitJoinRsp ( res , buf ) ;
2020-08-28 21:53:34 +01:00
case EZSP_messageSentHandler :
return EZ_MessageSent ( res , buf ) ;
2021-02-03 19:37:44 +00:00
case EZSP_energyScanResultHandler :
return EZSP_EnergyScanResult ( res , buf ) ;
case EZSP_scanCompleteHandler :
return EZSP_EnergyScanComplete ( res , buf ) ;
2020-06-29 21:21:32 +01:00
}
return - 1 ;
}
}
# endif // USE_ZIGBEE_EZSP
/*********************************************************************************************\
* Callbacks
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Publish the received values once they have been coalesced
2020-09-12 09:57:54 +01:00
void Z_PublishAttributes ( uint16_t shortaddr , uint16_t groupaddr , uint16_t cluster , uint8_t endpoint , uint32_t value ) {
2020-06-29 21:21:32 +01:00
zigbee_devices . jsonPublishFlush ( shortaddr ) ;
}
2020-07-02 21:56:37 +01:00
/*********************************************************************************************\
* Global dispatcher for incoming messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifdef USE_ZIGBEE_ZNP
2020-07-21 21:39:39 +01:00
//
// Callback for resetting the NCP, called by the state machine
//
// value = 0 : drive reset pin and halt MCU
// value = 1 : release the reset pin, restart
int32_t ZNP_Reset_Device ( uint8_t value ) {
2020-07-22 09:12:24 +01:00
/*
2020-07-21 21:39:39 +01:00
// we use Led4i to drive the reset pin. Since it is reverted we need to pass 1 to start reset, and 0 to release reset
if ( PinUsed ( GPIO_LED1 , ZIGBEE_EZSP_RESET_LED - 1 ) ) {
SetLedPowerIdx ( ZIGBEE_EZSP_RESET_LED - 1 , value ? 0 : 1 ) ;
2020-07-22 09:12:24 +01:00
*/
if ( PinUsed ( GPIO_ZIGBEE_RST ) ) {
digitalWrite ( Pin ( GPIO_ZIGBEE_RST ) , value ) ;
2020-07-21 21:39:39 +01:00
} else {
// no GPIO so we use software Reset instead
if ( value ) { // send reset only when we are supposed to release reset
// flush the serial buffer, sending 0xFF 256 times.
ZigbeeZNPFlush ( ) ;
ZigbeeZNPSend ( ZBS_RESET , sizeof ( ZBS_RESET ) ) ;
}
}
return 0 ; // continue
}
2021-01-25 15:02:56 +00:00
int32_t ZNP_ReceiveAfIncomingMessage ( int32_t res , const SBuffer & buf ) {
2020-07-02 21:56:37 +01:00
uint16_t groupid = buf . get16 ( 2 ) ;
uint16_t clusterid = buf . get16 ( 4 ) ;
uint16_t srcaddr = buf . get16 ( 6 ) ;
uint8_t srcendpoint = buf . get8 ( 8 ) ;
uint8_t dstendpoint = buf . get8 ( 9 ) ;
uint8_t wasbroadcast = buf . get8 ( 10 ) ;
uint8_t linkquality = buf . get8 ( 11 ) ;
uint8_t securityuse = buf . get8 ( 12 ) ;
// uint32_t timestamp = buf.get32(13);
uint8_t seqnumber = buf . get8 ( 17 ) ;
bool defer_attributes = false ; // do we defer attributes reporting to coalesce
ZCLFrame zcl_received = ZCLFrame : : parseRawFrame ( buf , 19 , buf . get8 ( 18 ) , clusterid , groupid ,
srcaddr ,
srcendpoint , dstendpoint , wasbroadcast ,
linkquality , securityuse , seqnumber ) ;
//
Z_IncomingMessage ( zcl_received ) ;
return - 1 ;
}
# endif // USE_ZIGBEE_ZNP
2020-06-29 21:21:32 +01:00
/*********************************************************************************************\
* Global dispatcher for incoming messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifdef USE_ZIGBEE_ZNP
2020-03-23 21:46:26 +00:00
// Structure for the Dispatcher callbacks table
2019-10-06 11:40:58 +01:00
typedef struct Z_Dispatcher {
2020-07-02 21:56:37 +01:00
uint8_t match [ 2 ] ;
2019-10-06 11:40:58 +01:00
ZB_RecvMsgFunc func ;
} Z_Dispatcher ;
2020-03-23 21:46:26 +00:00
// Dispatcher callbacks table
2019-10-06 11:40:58 +01:00
const Z_Dispatcher Z_DispatchTable [ ] PROGMEM = {
2020-07-02 21:56:37 +01:00
{ { Z_AREQ | Z_AF , AF_DATA_CONFIRM } , & ZNP_DataConfirm } , // 4480
{ { Z_AREQ | Z_AF , AF_INCOMING_MSG } , & ZNP_ReceiveAfIncomingMessage } , // 4481
// { { Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND }, &ZNP_ReceiveStateChange }, // 45C0
{ { Z_AREQ | Z_ZDO , ZDO_END_DEVICE_ANNCE_IND } , & Z_ReceiveEndDeviceAnnonce } , // 45C1
{ { Z_AREQ | Z_ZDO , ZDO_TC_DEV_IND } , & ZNP_ReceiveTCDevInd } , // 45CA
{ { Z_AREQ | Z_ZDO , ZDO_PERMIT_JOIN_IND } , & ZNP_ReceivePermitJoinStatus } , // 45CB
{ { Z_AREQ | Z_ZDO , ZDO_NODE_DESC_RSP } , & ZNP_ReceiveNodeDesc } , // 4582
{ { Z_AREQ | Z_ZDO , ZDO_ACTIVE_EP_RSP } , & Z_ReceiveActiveEp } , // 4585
2020-09-12 09:57:54 +01:00
{ { Z_AREQ | Z_ZDO , ZDO_SIMPLE_DESC_RSP } , & Z_ReceiveSimpleDesc } , // 4584
2020-07-02 21:56:37 +01:00
{ { Z_AREQ | Z_ZDO , ZDO_IEEE_ADDR_RSP } , & Z_ReceiveIEEEAddr } , // 4581
2020-07-05 20:01:26 +01:00
{ { Z_AREQ | Z_ZDO , ZDO_BIND_RSP } , & Z_BindRsp } , // 45A1
{ { Z_AREQ | Z_ZDO , ZDO_UNBIND_RSP } , & Z_UnbindRsp } , // 45A2
2020-10-28 16:30:46 +00:00
{ { Z_AREQ | Z_ZDO , ZDO_MGMT_LQI_RSP } , & Z_MgmtLqiRsp } , // 45B1
2020-07-05 20:01:26 +01:00
{ { Z_AREQ | Z_ZDO , ZDO_MGMT_BIND_RSP } , & Z_MgmtBindRsp } , // 45B3
2019-10-06 11:40:58 +01:00
} ;
2020-03-23 21:46:26 +00:00
/*********************************************************************************************\
* Default resolver
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-01-25 15:02:56 +00:00
int32_t ZNP_Recv_Default ( int32_t res , const SBuffer & buf ) {
2019-09-29 14:38:26 +01:00
// Default message handler for new messages
if ( zigbee . init_phase ) {
// if still during initialization phase, ignore any unexpected message
return - 1 ; // ignore message
} else {
2021-02-28 11:50:02 +00:00
for ( uint32_t i = 0 ; i < nitems ( Z_DispatchTable ) ; i + + ) {
2019-10-06 11:40:58 +01:00
if ( Z_ReceiveMatchPrefix ( buf , Z_DispatchTable [ i ] . match ) ) {
( * Z_DispatchTable [ i ] . func ) ( res , buf ) ;
}
2019-09-29 14:38:26 +01:00
}
return - 1 ;
}
}
2020-06-16 19:01:14 +01:00
# endif // USE_ZIGBEE_ZNP
2020-03-23 21:46:26 +00:00
/*********************************************************************************************\
* Functions called by State Machine
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-11-21 09:31:27 +00:00
//
// Callback for loading preparing EEPROM, called by the state machine
//
2021-04-22 13:49:38 +01:00
int32_t Z_Prepare_Storage ( uint8_t value ) {
# ifdef USE_ZIGBEE_EEPROM
2020-11-21 09:31:27 +00:00
ZFS : : initOrFormat ( ) ;
2021-04-22 13:49:38 +01:00
# endif
2020-11-21 09:31:27 +00:00
return 0 ; // continue
}
2020-03-23 21:46:26 +00:00
//
2021-04-22 13:49:38 +01:00
// Callback for loading Zigbee configuration, called by the state machine
2020-03-23 21:46:26 +00:00
//
2020-01-17 23:02:01 +00:00
int32_t Z_Load_Devices ( uint8_t value ) {
// try to hidrate from known devices
loadZigbeeDevices ( ) ;
return 0 ; // continue
}
2020-11-21 09:31:27 +00:00
//
// Callback for loading Zigbee data from EEPROM, called by the state machine
//
2021-04-22 13:49:38 +01:00
int32_t Z_Load_Data ( uint8_t value ) {
hydrateDevicesData ( ) ;
2020-11-21 09:31:27 +00:00
return 0 ; // continue
}
2020-03-26 18:34:59 +00:00
//
// Query the state of a bulb (light) if its type allows it
//
void Z_Query_Bulb ( uint16_t shortaddr , uint32_t & wait_ms ) {
const uint32_t inter_message_ms = 100 ; // wait 100ms between messages
if ( 0 < = zigbee_devices . getHueBulbtype ( shortaddr ) ) {
uint8_t endpoint = zigbee_devices . findFirstEndpoint ( shortaddr ) ;
if ( endpoint ) { // send only if we know the endpoint
2020-09-12 09:57:54 +01:00
zigbee_devices . setTimer ( shortaddr , 0 /* groupaddr */ , wait_ms , 0x0006 , endpoint , Z_CAT_READ_CLUSTER , 0 /* value */ , & Z_ReadAttrCallback ) ;
2020-03-26 18:34:59 +00:00
wait_ms + = inter_message_ms ;
2020-09-12 09:57:54 +01:00
zigbee_devices . setTimer ( shortaddr , 0 /* groupaddr */ , wait_ms , 0x0008 , endpoint , Z_CAT_READ_CLUSTER , 0 /* value */ , & Z_ReadAttrCallback ) ;
2020-03-26 18:34:59 +00:00
wait_ms + = inter_message_ms ;
2020-09-12 09:57:54 +01:00
zigbee_devices . setTimer ( shortaddr , 0 /* groupaddr */ , wait_ms , 0x0300 , endpoint , Z_CAT_READ_CLUSTER , 0 /* value */ , & Z_ReadAttrCallback ) ;
2020-03-26 18:34:59 +00:00
wait_ms + = inter_message_ms ;
zigbee_devices . setTimer ( shortaddr , 0 , wait_ms + Z_CAT_REACHABILITY_TIMEOUT , 0 , endpoint , Z_CAT_REACHABILITY , 0 /* value */ , & Z_Unreachable ) ;
2020-03-30 18:23:06 +01:00
wait_ms + = 1000 ; // wait 1 second between devices
2020-03-26 18:34:59 +00:00
}
}
}
2020-03-23 21:46:26 +00:00
//
// Send messages to query the state of each Hue emulated light
//
2020-03-14 13:17:30 +00:00
int32_t Z_Query_Bulbs ( uint8_t value ) {
2021-06-11 17:14:12 +01:00
if ( ! Settings - > flag5 . zb_disable_autoquery ) {
2020-11-14 10:15:41 +00:00
// Scan all devices and send deferred requests to know the state of bulbs
uint32_t wait_ms = 1000 ; // start with 1.0 s delay
for ( uint32_t i = 0 ; i < zigbee_devices . devicesSize ( ) ; i + + ) {
const Z_Device & device = zigbee_devices . devicesAt ( i ) ;
Z_Query_Bulb ( device . shortaddr , wait_ms ) ;
}
2020-03-14 13:17:30 +00:00
}
return 0 ; // continue
}
2020-03-23 21:46:26 +00:00
//
// Zigbee initialization is complete, let the party begin
//
2019-09-29 14:38:26 +01:00
int32_t Z_State_Ready ( uint8_t value ) {
zigbee . init_phase = false ; // initialization phase complete
return 0 ; // continue
}
2020-06-03 21:39:04 +01:00
//
// Auto-responder for Read request from extenal devices.
//
// Mostly used for routers/end-devices
// json: holds the attributes in JSON format
2020-10-19 19:34:40 +01:00
void ZCLFrame : : autoResponder ( const uint16_t * attr_list_ids , size_t attr_len ) {
2020-09-14 21:06:19 +01:00
Z_attribute_list attr_list ;
2020-06-03 21:39:04 +01:00
2020-09-05 13:44:31 +01:00
for ( uint32_t i = 0 ; i < attr_len ; i + + ) {
2020-09-14 21:06:19 +01:00
uint16_t attr_id = attr_list_ids [ i ] ;
2022-05-16 18:17:40 +01:00
uint32_t ccccaaaa = ( cluster < < 16 ) | attr_id ;
2020-09-14 21:06:19 +01:00
Z_attribute attr ;
2022-05-16 18:17:40 +01:00
attr . setKeyId ( cluster , attr_id ) ;
2020-09-05 13:44:31 +01:00
switch ( ccccaaaa ) {
2020-09-14 21:06:19 +01:00
case 0x00000004 : attr . setStr ( PSTR ( USE_ZIGBEE_MANUFACTURER ) ) ; break ; // Manufacturer
case 0x00000005 : attr . setStr ( PSTR ( USE_ZIGBEE_MODELID ) ) ; break ; // ModelId
2020-06-03 21:39:04 +01:00
# ifdef USE_LIGHT
2020-09-14 21:06:19 +01:00
case 0x00060000 : attr . setUInt ( Light . power ? 1 : 0 ) ; break ; // Power
case 0x00080000 : attr . setUInt ( LightGetDimmer ( 0 ) ) ; break ; // Dimmer
2020-09-05 13:44:31 +01:00
case 0x03000000 : // Hue
case 0x03000001 : // Sat
case 0x03000003 : // X
case 0x03000004 : // Y
case 0x03000007 : // CT
{
uint16_t hue ;
uint8_t sat ;
float XY [ 2 ] ;
LightGetHSB ( & hue , & sat , nullptr ) ;
LightGetXY ( & XY [ 0 ] , & XY [ 1 ] ) ;
uint16_t uxy [ 2 ] ;
2021-02-28 11:50:02 +00:00
for ( uint32_t i = 0 ; i < nitems ( XY ) ; i + + ) {
2020-09-05 13:44:31 +01:00
uxy [ i ] = XY [ i ] * 65536.0f ;
uxy [ i ] = ( uxy [ i ] > 0xFEFF ) ? uxy [ i ] : 0xFEFF ;
}
2020-09-14 21:06:19 +01:00
if ( 0x0000 = = attr_id ) { attr . setUInt ( changeUIntScale ( hue , 0 , 360 , 0 , 254 ) ) ; }
if ( 0x0001 = = attr_id ) { attr . setUInt ( changeUIntScale ( sat , 0 , 255 , 0 , 254 ) ) ; }
if ( 0x0003 = = attr_id ) { attr . setUInt ( uxy [ 0 ] ) ; }
if ( 0x0004 = = attr_id ) { attr . setUInt ( uxy [ 1 ] ) ; }
if ( 0x0007 = = attr_id ) { attr . setUInt ( LightGetColorTemp ( ) ) ; }
2020-09-05 13:44:31 +01:00
}
break ;
2020-06-03 21:39:04 +01:00
# endif
2020-09-05 13:44:31 +01:00
case 0x000A0000 : // Time
2020-11-08 08:38:34 +00:00
attr . setUInt ( ( Rtc . utc_time > START_VALID_TIME ) ? Rtc . utc_time - 946684800 : Rtc . utc_time ) ;
2020-09-05 13:44:31 +01:00
break ;
case 0x000AFF00 : // TimeEpoch - Tasmota specific
2020-09-14 21:06:19 +01:00
attr . setUInt ( Rtc . utc_time ) ;
2020-09-05 13:44:31 +01:00
break ;
case 0x000A0001 : // TimeStatus
2020-11-08 08:38:34 +00:00
attr . setUInt ( ( Rtc . utc_time > START_VALID_TIME ) ? 0x02 : 0x00 ) ;
2020-09-05 13:44:31 +01:00
break ;
case 0x000A0002 : // TimeZone
2021-06-11 17:14:12 +01:00
attr . setUInt ( Settings - > toffset [ 0 ] * 60 ) ;
2020-09-05 13:44:31 +01:00
break ;
case 0x000A0007 : // LocalTime // TODO take DST
2021-06-11 17:14:12 +01:00
attr . setUInt ( Settings - > toffset [ 0 ] * 60 + ( ( Rtc . utc_time > START_VALID_TIME ) ? Rtc . utc_time - 946684800 : Rtc . utc_time ) ) ;
2020-09-05 13:44:31 +01:00
break ;
}
2020-09-14 21:06:19 +01:00
if ( ! attr . isNone ( ) ) {
2022-05-16 18:17:40 +01:00
Z_parseAttributeKey ( attr , cluster ) ;
attr_list . addAttribute ( cluster , attr_id ) = attr ;
2020-09-14 21:06:19 +01:00
}
2020-06-03 21:39:04 +01:00
}
2020-09-14 21:06:19 +01:00
SBuffer buf ( 200 ) ;
for ( const auto & attr : attr_list ) {
if ( ! ZbAppendWriteBuf ( buf , attr , true ) ) { // true = need status indicator in Read Attribute Responses
return ; // error
}
}
if ( buf . len ( ) > 0 ) {
2020-06-03 21:39:04 +01:00
// we have a non-empty output
// log first
2021-06-05 10:47:09 +01:00
AddLog ( LOG_LEVEL_INFO , PSTR ( " ZIG: Auto-responder: ZbSend { \" Device \" : \" 0x%04X \" "
2020-06-03 21:39:04 +01:00
" , \" Cluster \" : \" 0x%04X \" "
" , \" Endpoint \" :%d "
" , \" Response \" :%s} "
) ,
2022-05-16 18:17:40 +01:00
shortaddr , cluster , _srcendpoint ,
2020-09-14 21:06:19 +01:00
attr_list . toString ( ) . c_str ( ) ) ;
2020-06-03 21:39:04 +01:00
// send
2020-09-14 21:06:19 +01:00
// all good, send the packet
2022-05-16 18:17:40 +01:00
ZCLFrame zcl ( buf . len ( ) ) ; // message is 4 bytes
zcl . shortaddr = shortaddr ;
zcl . cluster = cluster ;
zcl . dstendpoint = _srcendpoint ;
2021-02-02 19:46:18 +00:00
zcl . cmd = ZCL_READ_ATTRIBUTES_RESPONSE ;
zcl . clusterSpecific = false ; /* not cluster specific */
zcl . needResponse = false ; /* noresponse */
zcl . direct = true ; /* direct response */
2022-05-16 18:17:40 +01:00
zcl . setTransac ( transactseq ) ;
zcl . payload . addBuffer ( buf ) ;
2021-02-02 19:46:18 +00:00
zigbeeZCLSendCmd ( zcl ) ;
2020-06-03 21:39:04 +01:00
}
}
2019-09-29 14:38:26 +01:00
# endif // USE_ZIGBEE