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
2019-12-31 13:23:34 +00:00
Copyright ( C ) 2020 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-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 ; }
return changeUIntScale ( rssi + 87 , 0 , 87 + 10 , 0 , 255 ) ;
}
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
int32_t 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 " \" :{ "
" \" Status \" :%d, \" Message \" : \" EFR32 booted \" , \" RestartReason \" : \" %s \" "
" , \" 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
int32_t 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
}
2020-07-02 21:56:37 +01:00
int32_t EZ_ReadAPSUnicastMessage ( int32_t res , class SBuffer & buf ) {
2020-06-19 19:54:37 +01:00
// Called when receiving a response from getConfigurationValue
// Value is in bytes 2+3
uint16_t value = buf . get16 ( 2 ) ;
return res ;
}
2020-07-02 21:56:37 +01:00
/*********************************************************************************************\
* Parsers for incoming EZSP messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//
// Handle a "getEui64" incoming message
//
int32_t EZ_GetEUI64 ( int32_t res , class SBuffer & buf ) {
localIEEEAddr = buf . get64 ( 2 ) ;
return res ;
}
//
// Handle a "getEui64" incoming message
//
int32_t EZ_GetNodeId ( int32_t res , class SBuffer & buf ) {
localShortAddr = buf . get8 ( 2 ) ;
return res ;
}
//
// Handle a "getNetworkParameters" incoming message
//
int32_t EZ_NetworkParameters ( int32_t res , class SBuffer & buf ) {
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;
char hex [ 20 ] ;
Uint64toHex ( localIEEEAddr , hex , 64 ) ;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
" \" Status \" :%d, \" IEEEAddr \" : \" 0x%s \" , \" ShortAddr \" : \" 0x%04X \" "
" , \" DeviceType \" :%d}} " ) ,
ZIGBEE_STATUS_EZ_INFO , hex , localShortAddr , node_type ) ;
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
//
int32_t EZ_CheckKeyNWK ( int32_t res , class SBuffer & buf ) {
uint8_t status = buf . get8 ( 2 ) ;
uint16_t bitmask = buf . get16 ( 3 ) ;
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
//
int32_t EZ_RouteError ( int32_t res , const class SBuffer & buf ) {
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 ;
}
//
// Handle a "permitJoining" incoming message
//
int32_t EZ_PermitJoinRsp ( int32_t res , const class SBuffer & buf ) {
uint8_t status = buf . get8 ( 2 ) ;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
" \" Status \" :%d, \" Message \" : \" %s " ) ,
( 0 = = status ) ? ZIGBEE_STATUS_PERMITJOIN_OPEN_60 : ZIGBEE_STATUS_PERMITJOIN_CLOSE ,
( 0 = = status ) ? PSTR ( " Pairing mode enabled " ) : PSTR ( " Pairing mode error " )
) ;
if ( status ) {
ResponseAppend_P ( " 0x%02X " , status ) ;
}
ResponseAppend_P ( PSTR ( " \" }} " ) ) ;
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
return - 1 ;
}
2020-06-19 19:54:37 +01:00
# endif // USE_ZIGBEE_EZSP
2020-06-29 21:21:32 +01:00
/*********************************************************************************************\
* Parsers for incoming EZSP messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//
// Handle a "getEui64" incoming message
//
int32_t Z_EZSPGetEUI64 ( int32_t res , class SBuffer & buf ) {
localIEEEAddr = buf . get64 ( 2 ) ;
return res ;
}
//
// Handle a "getEui64" incoming message
//
int32_t Z_EZSPGetNodeId ( int32_t res , class SBuffer & buf ) {
localShortAddr = buf . get8 ( 2 ) ;
return res ;
}
//
// Handle a "getNetworkParameters" incoming message
//
int32_t Z_EZSPNetworkParameters ( int32_t res , class SBuffer & buf ) {
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;
char hex [ 20 ] ;
Uint64toHex ( localIEEEAddr , hex , 64 ) ;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
" \" Status \" :%d, \" IEEEAddr \" : \" 0x%s \" , \" ShortAddr \" : \" 0x%04X \" "
" , \" DeviceType \" :%d}} " ) ,
ZIGBEE_STATUS_EZ_INFO , hex , localShortAddr , node_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
return res ;
}
2020-03-23 21:46:26 +00:00
/*********************************************************************************************\
* Parsers for incoming ZNP messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//
// Handle a "Receive Device Info" incoming message
//
2020-07-02 21:56:37 +01:00
int32_t ZNP_ReceiveDeviceInfo ( int32_t res , class 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-09-29 14:38:26 +01:00
char hex [ 20 ] ;
Uint64toHex ( long_adr , hex , 64 ) ;
2019-11-09 08:25:15 +00:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2020-03-01 10:25:59 +00:00
" \" Status \" :%d, \" IEEEAddr \" : \" 0x%s \" , \" ShortAddr \" : \" 0x%04X \" "
2019-09-29 14:38:26 +01:00
" , \" DeviceType \" :%d, \" DeviceState \" :%d "
" , \" NumAssocDevices \" :%d " ) ,
ZIGBEE_STATUS_CC_INFO , hex , short_adr , device_type , device_state ,
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 ;
}
2020-07-02 21:56:37 +01:00
int32_t ZNP_CheckNVWrite ( int32_t res , class 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
}
}
2020-07-02 21:56:37 +01:00
int32_t ZNP_Reboot ( int32_t res , class 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 ) ;
uint8_t transport_rev = buf . get8 ( 3 ) ;
uint8_t product_id = buf . get8 ( 4 ) ;
uint8_t major_rel = buf . get8 ( 5 ) ;
uint8_t minor_rel = buf . get8 ( 6 ) ;
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 " \" :{ "
2020-03-16 17:55:58 +00:00
" \" Status \" :%d, \" Message \" : \" CC2530 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
if ( ( 0x02 = = major_rel ) & & ( 0x06 = = minor_rel ) ) {
return 0 ; // version 2.6.x is ok
} else {
return ZIGBEE_LABEL_UNSUPPORTED_VERSION ; // abort
}
}
2020-06-29 21:21:32 +01:00
# ifdef USE_ZIGBEE_ZNP
2020-07-02 21:56:37 +01:00
int32_t ZNP_ReceiveCheckVersion ( int32_t res , class 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)
uint8_t major_rel = buf . get8 ( 4 ) ;
uint8_t minor_rel = buf . get8 ( 5 ) ;
uint8_t maint_rel = buf . get8 ( 6 ) ;
uint32_t revision = buf . get32 ( 7 ) ;
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}} " ) ,
ZIGBEE_STATUS_CC_VERSION , major_rel , minor_rel ,
maint_rel , revision ) ;
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
if ( ( 0x02 = = major_rel ) & & ( 0x06 = = minor_rel ) ) {
return 0 ; // version 2.6.x is ok
} 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
2020-07-02 21:56:37 +01:00
int32_t EZ_ReceiveCheckVersion ( int32_t res , class SBuffer & buf ) {
2020-06-29 21:21:32 +01:00
uint8_t protocol_version = buf . get8 ( 2 ) ;
uint8_t stack_type = buf . get8 ( 3 ) ;
uint16_t stack_version = buf . get16 ( 4 ) ;
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 ,
2020-06-29 21:21:32 +01:00
( stack_version & 0xF000 ) > > 12 ,
( stack_version & 0x0F00 ) > > 8 ,
( stack_version & 0x00F0 ) > > 4 ,
stack_version & 0x000F ,
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 ) {
return 0 ; // protocol v8 is ok
} else {
return ZIGBEE_LABEL_UNSUPPORTED_VERSION ; // abort
}
2019-09-29 14:38:26 +01:00
}
2020-07-02 21:56:37 +01:00
static bool EZ_reset_config = false ;
// 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
int32_t Z_SwitchDeviceType ( int32_t res , class SBuffer & buf ) {
switch ( Settings . zb_pan_id ) {
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
//
2019-09-29 14:38:26 +01:00
bool Z_ReceiveMatchPrefix ( const class SBuffer & buf , const uint8_t * match ) {
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
//
2020-07-02 21:56:37 +01:00
int32_t ZNP_ReceivePermitJoinStatus ( int32_t res , const class 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 ;
if ( 0xFF = = duration ) {
status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_XX ;
message = PSTR ( " Enable Pairing mode until next boot " ) ;
} else if ( duration > 0 ) {
status_code = ZIGBEE_STATUS_PERMITJOIN_OPEN_60 ;
message = PSTR ( " Enable Pairing mode for %d seconds " ) ;
} else {
status_code = ZIGBEE_STATUS_PERMITJOIN_CLOSE ;
message = PSTR ( " Disable Pairing mode " ) ;
}
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
//
int32_t ZNP_ReceiveNodeDesc ( int32_t res , const class SBuffer & buf ) {
2019-09-29 14:38:26 +01:00
// Received ZDO_NODE_DESC_RSP
Z_ShortAddress srcAddr = buf . get16 ( 2 ) ;
uint8_t status = buf . get8 ( 4 ) ;
Z_ShortAddress nwkAddr = buf . get16 ( 5 ) ;
uint8_t logicalType = buf . get8 ( 7 ) ;
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 ) ;
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
//
2019-09-29 14:38:26 +01:00
int32_t Z_ReceiveActiveEp ( int32_t res , const class SBuffer & buf ) {
// Received ZDO_ACTIVE_EP_RSP
2020-07-02 21:56:37 +01:00
# ifdef USE_ZIGBEE_ZNP
// Z_ShortAddress srcAddr = buf.get16(2);
2019-09-29 14:38:26 +01:00
uint8_t status = buf . get8 ( 4 ) ;
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
uint8_t status = buf . get8 ( 0 ) ;
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
for ( uint32_t i = 0 ; i < activeEpCount ; i + + ) {
2020-03-17 17:46:05 +00:00
zigbee_devices . addEndpoint ( nwkAddr , activeEpList [ i ] ) ;
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
Z_SendAFInfoRequest ( nwkAddr ) ; // probe for ModelId and ManufId
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
2020-02-23 15:46:00 +00:00
int32_t Z_ReceiveIEEEAddr ( int32_t res , const class 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 ) ;
char hex [ 20 ] ;
Uint64toHex ( ieeeAddr , hex , 64 ) ;
// 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 \" "
" , \" " D_JSON_ZIGBEE_IEEE " \" : \" 0x%s \" " ) , nwkAddr , hex ) ;
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-05-17 17:33:42 +01: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"}}
//
2020-07-02 21:56:37 +01:00
int32_t ZNP_DataConfirm ( int32_t res , const class 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
2020-07-02 21:56:37 +01:00
int32_t ZNP_ReceiveStateChange ( int32_t res , const class 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
//
2019-09-29 14:38:26 +01:00
int32_t Z_ReceiveEndDeviceAnnonce ( int32_t res , const class 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
2019-10-06 11:40:58 +01:00
zigbee_devices . updateDevice ( nwkAddr , ieeeAddr ) ;
2019-09-29 14:38:26 +01:00
char hex [ 20 ] ;
Uint64toHex ( ieeeAddr , hex , 64 ) ;
2019-11-09 08:25:15 +00:00
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2020-03-17 17:46:05 +00:00
" \" Status \" :%d, \" IEEEAddr \" : \" 0x%s \" , \" ShortAddr \" : \" 0x%04X \" "
2019-09-29 14:38:26 +01:00
" , \" PowerSource \" :%s, \" ReceiveWhenIdle \" :%s, \" Security \" :%s}} " ) ,
ZIGBEE_STATUS_DEVICE_ANNOUNCE , hex , 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 ) ) ;
2019-09-29 14:38:26 +01:00
Z_SendActiveEpReq ( nwkAddr ) ;
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
//
2020-07-02 21:56:37 +01:00
int32_t ZNP_ReceiveTCDevInd ( int32_t res , const class 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 ) ;
char hex [ 20 ] ;
Uint64toHex ( ieeeAddr , hex , 64 ) ;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
2020-03-17 17:46:05 +00:00
" \" Status \" :%d, \" IEEEAddr \" : \" 0x%s \" , \" ShortAddr \" : \" 0x%04X \" "
2019-12-23 15:53:54 +00:00
" , \" ParentNetwork \" : \" 0x%04X \" }} " ) ,
ZIGBEE_STATUS_DEVICE_INDICATION , hex , srcAddr , parentNw
) ;
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
//
2020-07-05 20:01:26 +01:00
int32_t Z_BindRsp ( int32_t res , const class SBuffer & buf ) {
# 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
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
//
2020-07-05 20:01:26 +01:00
int32_t Z_UnbindRsp ( int32_t res , const class SBuffer & buf ) {
# 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
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-03-30 18:23:06 +01:00
//
// Handle MgMt Bind Rsp incoming message
//
2020-07-05 20:01:26 +01:00
int32_t Z_MgmtBindRsp ( int32_t res , const class SBuffer & buf ) {
# ifdef USE_ZIGBEE_ZNP
2020-03-30 18:23:06 +01:00
uint16_t shortaddr = buf . get16 ( 2 ) ;
uint8_t status = buf . get8 ( 4 ) ;
uint8_t bind_total = buf . get8 ( 5 ) ;
uint8_t bind_start = buf . get8 ( 6 ) ;
uint8_t bind_len = buf . get8 ( 7 ) ;
2020-07-05 20:01:26 +01: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 ) ;
uint8_t bind_total = buf . get8 ( 1 ) ;
uint8_t bind_start = buf . get8 ( 2 ) ;
uint8_t bind_len = buf . get8 ( 3 ) ;
const size_t prefix_len = 4 ;
# endif // USE_ZIGBEE_EZSP
2020-03-30 18:23:06 +01:00
const char * friendlyName = zigbee_devices . getFriendlyName ( shortaddr ) ;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_BIND_STATE " \" :{ \" " D_JSON_ZIGBEE_DEVICE " \" : \" 0x%04X \" " ) , shortaddr ) ;
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 \" "
" , \" BindingsTotal \" :%d "
2020-07-29 09:02:04 +01:00
" , \" BindingsStart \" :%d "
2020-03-30 18:23:06 +01:00
" , \" Bindings \" :[ "
2020-07-29 09:02:04 +01:00
) , status , getZigbeeStatusMessage ( status ) . c_str ( ) , bind_total , bind_start + 1 ) ;
2020-03-30 18:23:06 +01:00
2020-07-05 20:01:26 +01:00
uint32_t idx = prefix_len ;
2020-03-30 18:23:06 +01:00
for ( uint32_t i = 0 ; i < bind_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 ) ;
uint8_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 {
2020-07-02 21:56:37 +01:00
//AddLog_P2(LOG_LEVEL_INFO, PSTR("ZNP_MgmtBindRsp unknwon address mode %d"), addrmode);
2020-03-30 18:23:06 +01:00
break ; // abort for any other value since we don't know the length of the field
}
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
char hex [ 20 ] ;
Uint64toHex ( dstaddr , hex , 64 ) ;
ResponseAppend_P ( PSTR ( " \" ToDevice \" : \" 0x%s \" , \" ToEndpoint \" :%d} " ) , hex , dstep ) ;
}
}
ResponseAppend_P ( PSTR ( " ]}} " ) ) ;
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_BIND_STATE ) ) ;
2020-03-30 18:23:06 +01:00
return - 1 ;
}
2020-03-23 21:46:26 +00:00
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
2020-07-26 12:54:24 +01:00
int32_t EZ_ParentAnnceRsp ( int32_t res , const class 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 ;
}
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 ( " , " ) ) ;
}
char hex [ 20 ] ;
Uint64toHex ( child_ieee , hex , 64 ) ;
ResponseAppend_P ( PSTR ( " \" 0x%s \" " ) , hex ) ;
}
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
}
//
// Send AF Info Request
//
void Z_SendAFInfoRequest ( uint16_t shortaddr ) {
uint8_t endpoint = zigbee_devices . findFirstEndpoint ( shortaddr ) ;
if ( 0x00 = = endpoint ) { endpoint = 0x01 ; } // if we don't know the endpoint, try 0x01
uint8_t transacid = zigbee_devices . getNextSeqNumber ( shortaddr ) ;
2020-07-02 21:56:37 +01:00
uint8_t InfoReq [ ] = { 0x04 , 0x00 , 0x05 , 0x00 } ;
ZigbeeZCLSend_Raw ( shortaddr , 0x0000 /*group*/ , 0x0000 /*cluster*/ , endpoint , ZCL_READ_ATTRIBUTES ,
false /*clusterSpecific*/ , 0x0000 /*manuf*/ ,
InfoReq , sizeof ( InfoReq ) , true /*needResponse*/ , transacid ) ;
2020-03-23 21:46:26 +00: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
int32_t EZ_ReceiveTCJoinHandler ( int32_t res , const class SBuffer & buf ) {
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
char hex [ 20 ] ;
Uint64toHex ( ieeeAddr , hex , 64 ) ;
Response_P ( PSTR ( " { \" " D_JSON_ZIGBEE_STATE " \" :{ "
" \" Status \" :%d, \" IEEEAddr \" : \" 0x%s \" , \" ShortAddr \" : \" 0x%04X \" "
" , \" ParentNetwork \" : \" 0x%04X \" "
" , \" Status \" :%d, \" Decision \" :%d "
" }} " ) ,
ZIGBEE_STATUS_DEVICE_INDICATION , hex , srcAddr , parentNw ,
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-06-29 21:21:32 +01:00
void Z_IncomingMessage ( ZCLFrame & zcl_received ) {
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 ( ) ;
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
2020-06-29 21:21:32 +01:00
zigbee_devices . setLQI ( srcaddr , linkquality ! = 0xFF ? linkquality : 0xFE ) ; // EFR32 has a different scale for LQI
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
2019-09-29 14:38:26 +01:00
DynamicJsonBuffer jsonBuffer ;
2020-01-19 21:59:02 +00:00
JsonObject & json = jsonBuffer . createObject ( ) ;
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-05-29 21:52:45 +01:00
zcl_received . parseResponse ( ) ; // Zigbee general "Degault Response", publish ZbResponse message
2020-06-27 17:17:40 +01:00
} else {
2020-03-01 10:25:59 +00:00
// Build the ZbReceive json
if ( ( ! zcl_received . isClusterSpecificCommand ( ) ) & & ( ZCL_REPORT_ATTRIBUTES = = zcl_received . getCmdId ( ) ) ) {
2020-06-03 21:39:04 +01:00
zcl_received . parseReportAttributes ( json ) ; // Zigbee report attributes from sensors
2020-03-01 10:25:59 +00:00
if ( clusterid ) { defer_attributes = true ; } // don't defer system Cluster=0 messages
} else if ( ( ! zcl_received . isClusterSpecificCommand ( ) ) & & ( ZCL_READ_ATTRIBUTES_RESPONSE = = zcl_received . getCmdId ( ) ) ) {
2020-05-29 21:52:45 +01:00
zcl_received . parseReadAttributesResponse ( json ) ;
if ( clusterid ) { defer_attributes = true ; } // don't defer system Cluster=0 messages
} else if ( ( ! zcl_received . isClusterSpecificCommand ( ) ) & & ( ZCL_READ_ATTRIBUTES = = zcl_received . getCmdId ( ) ) ) {
2020-03-01 10:25:59 +00:00
zcl_received . parseReadAttributes ( json ) ;
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-03-01 10:25:59 +00:00
} else if ( zcl_received . isClusterSpecificCommand ( ) ) {
zcl_received . parseClusterSpecificCommand ( json ) ;
}
2020-06-03 21:39:04 +01:00
{ // fence to force early de-allocation of msg
String msg ( " " ) ;
msg . reserve ( 100 ) ;
json . printTo ( msg ) ;
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_ZIGBEE D_JSON_ZIGBEEZCL_RAW_RECEIVED " : { \" 0x%04X \" :%s} " ) , srcaddr , msg . 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 ) {
AddLog_P2 ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_ZIGBEE " loopback message, ignoring " ) ) ;
return ; // abort the rest of message management
}
2020-03-01 10:25:59 +00:00
zcl_received . postProcessAttributes ( srcaddr , json ) ;
// Add Endpoint
json [ F ( D_CMND_ZIGBEE_ENDPOINT ) ] = srcendpoint ;
// Add Group if non-zero
if ( groupid ) {
json [ F ( D_CMND_ZIGBEE_GROUP ) ] = groupid ;
}
// Add linkquality
json [ F ( D_CMND_ZIGBEE_LINKQUALITY ) ] = linkquality ;
2020-03-26 18:34:59 +00:00
// since we just receveived data from the device, it is reachable
zigbee_devices . resetTimersForDevice ( srcaddr , 0 /* groupaddr */ , Z_CAT_REACHABILITY ) ; // remove any reachability timer already there
zigbee_devices . setReachable ( srcaddr , true ) ; // mark device as reachable
2020-04-08 14:20:04 +01:00
// Post-provess for Aqara Presence Senson
Z_AqaraOccupancy ( srcaddr , clusterid , srcendpoint , json ) ;
2020-03-01 10:25:59 +00:00
if ( defer_attributes ) {
// Prepare for publish
if ( zigbee_devices . jsonIsConflict ( srcaddr , json ) ) {
// there is conflicting values, force a publish of the previous message now and don't coalesce
zigbee_devices . jsonPublishFlush ( srcaddr ) ;
}
zigbee_devices . jsonAppend ( srcaddr , json ) ;
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
zigbee_devices . jsonPublishNow ( srcaddr , json ) ;
2020-06-03 21:39:04 +01:00
// Add auto-responder here
Z_AutoResponder ( srcaddr , clusterid , srcendpoint , json [ F ( " ReadNames " ) ] ) ;
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
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void EZ_SendZDO ( uint16_t shortaddr , uint16_t cmd , const unsigned char * payload , size_t payload_len ) {
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
buf . add16 ( EMBER_APS_OPTION_ENABLE_ROUTE_DISCOVERY | EMBER_APS_OPTION_RETRY ) ; // APS frame
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
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int32_t EZ_IncomingMessage ( int32_t res , const class SBuffer & buf ) {
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
// uint8_t linkquality = buf.get8(14);
int8_t linkrssi = buf . get8 ( 15 ) ;
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-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-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 ) ;
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 :
// TODO move later to LOG_LEVEL_DEBUG
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " ZIG: Internal ZDO message 0x%04X sent from 0x%04X %s " ) , clusterid , srcaddr , wasbroadcast ? PSTR ( " (broadcast) " ) : " " ) ;
break ;
2020-06-29 21:21:32 +01:00
}
} else {
bool defer_attributes = false ; // do we defer attributes reporting to coalesce
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
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-07-02 21:56:37 +01:00
int32_t EZ_Recv_Default ( int32_t res , const class 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 ) ;
break ;
case EZSP_trustCenterJoinHandler :
return EZ_ReceiveTCJoinHandler ( res , buf ) ;
break ;
2020-07-25 14:40:42 +01:00
case EZSP_incomingRouteErrorHandler :
return EZ_RouteError ( res , buf ) ;
break ;
case EZSP_permitJoining :
return EZ_PermitJoinRsp ( res , buf ) ;
break ;
2020-06-29 21:21:32 +01:00
}
return - 1 ;
}
}
# endif // USE_ZIGBEE_EZSP
/*********************************************************************************************\
* Callbacks
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// Aqara Occupancy behavior: the Aqara device only sends Occupancy: true events every 60 seconds.
// Here we add a timer so if we don't receive a Occupancy event for 90 seconds, we send Occupancy:false
void Z_AqaraOccupancy ( uint16_t shortaddr , uint16_t cluster , uint8_t endpoint , const JsonObject & json ) {
static const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000 ; // 90 s
// Read OCCUPANCY value if any
const JsonVariant & val_endpoint = GetCaseInsensitive ( json , PSTR ( OCCUPANCY ) ) ;
if ( nullptr ! = & val_endpoint ) {
uint32_t occupancy = strToUInt ( val_endpoint ) ;
if ( occupancy ) {
zigbee_devices . setTimer ( shortaddr , 0 /* groupaddr */ , OCCUPANCY_TIMEOUT , cluster , endpoint , Z_CAT_VIRTUAL_OCCUPANCY , 0 , & Z_OccupancyCallback ) ;
} else {
zigbee_devices . resetTimersForDevice ( shortaddr , 0 /* groupaddr */ , Z_CAT_VIRTUAL_OCCUPANCY ) ;
}
}
}
// Publish the received values once they have been coalesced
int32_t Z_PublishAttributes ( uint16_t shortaddr , uint16_t groupaddr , uint16_t cluster , uint8_t endpoint , uint32_t value ) {
const JsonObject * json = zigbee_devices . jsonGet ( shortaddr ) ;
if ( json = = nullptr ) { return 0 ; } // don't crash if not found
zigbee_devices . jsonPublishFlush ( shortaddr ) ;
return 1 ;
}
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
}
2020-07-02 21:56:37 +01:00
int32_t ZNP_ReceiveAfIncomingMessage ( int32_t res , const class SBuffer & buf ) {
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
{ { 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
{ { 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
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-07-02 21:56:37 +01:00
int32_t ZNP_Recv_Default ( int32_t res , const class 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 {
2020-06-29 21:21:32 +01:00
for ( uint32_t i = 0 ; i < ARRAY_SIZE ( 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
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//
// Callback for loading Zigbee configuration from Flash, called by the state machine
//
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-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
zigbee_devices . setTimer ( shortaddr , 0 /* groupaddr */ , wait_ms , 0x0006 , endpoint , Z_CAT_NONE , 0 /* value */ , & Z_ReadAttrCallback ) ;
wait_ms + = inter_message_ms ;
zigbee_devices . setTimer ( shortaddr , 0 /* groupaddr */ , wait_ms , 0x0008 , endpoint , Z_CAT_NONE , 0 /* value */ , & Z_ReadAttrCallback ) ;
wait_ms + = inter_message_ms ;
zigbee_devices . setTimer ( shortaddr , 0 /* groupaddr */ , wait_ms , 0x0300 , endpoint , Z_CAT_NONE , 0 /* value */ , & Z_ReadAttrCallback ) ;
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 ) {
// 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 ) ;
2020-03-26 18:34:59 +00:00
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
void Z_AutoResponder ( uint16_t srcaddr , uint16_t cluster , uint8_t endpoint , const JsonObject & json ) {
DynamicJsonBuffer jsonBuffer ;
JsonObject & json_out = jsonBuffer . createObject ( ) ;
// responder
switch ( cluster ) {
case 0x0000 :
2020-06-15 19:13:05 +01:00
if ( HasKeyCaseInsensitive ( json , PSTR ( " ModelId " ) ) ) { json_out [ F ( " ModelId " ) ] = F ( USE_ZIGBEE_MODELID ) ; }
if ( HasKeyCaseInsensitive ( json , PSTR ( " Manufacturer " ) ) ) { json_out [ F ( " Manufacturer " ) ] = F ( USE_ZIGBEE_MANUFACTURER ) ; }
2020-06-03 21:39:04 +01:00
break ;
# ifdef USE_LIGHT
case 0x0006 :
if ( HasKeyCaseInsensitive ( json , PSTR ( " Power " ) ) ) { json_out [ F ( " Power " ) ] = Light . power ? 1 : 0 ; }
break ;
case 0x0008 :
if ( HasKeyCaseInsensitive ( json , PSTR ( " Dimmer " ) ) ) { json_out [ F ( " Dimmer " ) ] = LightGetDimmer ( 0 ) ; }
break ;
case 0x0300 :
{
uint16_t hue ;
uint8_t sat ;
float XY [ 2 ] ;
LightGetHSB ( & hue , & sat , nullptr ) ;
LightGetXY ( & XY [ 0 ] , & XY [ 1 ] ) ;
uint16_t uxy [ 2 ] ;
for ( uint32_t i = 0 ; i < ARRAY_SIZE ( XY ) ; i + + ) {
uxy [ i ] = XY [ i ] * 65536.0f ;
uxy [ i ] = ( uxy [ i ] > 0xFEFF ) ? uxy [ i ] : 0xFEFF ;
}
if ( HasKeyCaseInsensitive ( json , PSTR ( " Hue " ) ) ) { json_out [ F ( " Hue " ) ] = changeUIntScale ( hue , 0 , 360 , 0 , 254 ) ; }
if ( HasKeyCaseInsensitive ( json , PSTR ( " Sat " ) ) ) { json_out [ F ( " Sat " ) ] = changeUIntScale ( sat , 0 , 255 , 0 , 254 ) ; }
if ( HasKeyCaseInsensitive ( json , PSTR ( " CT " ) ) ) { json_out [ F ( " CT " ) ] = LightGetColorTemp ( ) ; }
if ( HasKeyCaseInsensitive ( json , PSTR ( " X " ) ) ) { json_out [ F ( " X " ) ] = uxy [ 0 ] ; }
if ( HasKeyCaseInsensitive ( json , PSTR ( " Y " ) ) ) { json_out [ F ( " Y " ) ] = uxy [ 1 ] ; }
}
break ;
# endif
case 0x000A : // Time
2020-06-05 21:27:47 +01:00
if ( HasKeyCaseInsensitive ( json , PSTR ( " Time " ) ) ) { json_out [ F ( " Time " ) ] = ( Rtc . utc_time > ( 60 * 60 * 24 * 365 * 10 ) ) ? Rtc . utc_time - 946684800 : Rtc . utc_time ; }
if ( HasKeyCaseInsensitive ( json , PSTR ( " TimeEpoch " ) ) ) { json_out [ F ( " TimeEpoch " ) ] = Rtc . utc_time ; }
2020-06-03 21:39:04 +01:00
if ( HasKeyCaseInsensitive ( json , PSTR ( " TimeStatus " ) ) ) { json_out [ F ( " TimeStatus " ) ] = ( Rtc . utc_time > ( 60 * 60 * 24 * 365 * 10 ) ) ? 0x02 : 0x00 ; } // if time is beyond 2010 then we are synchronized
if ( HasKeyCaseInsensitive ( json , PSTR ( " TimeZone " ) ) ) { json_out [ F ( " TimeZone " ) ] = Settings . toffset [ 0 ] * 60 ; } // seconds
break ;
}
if ( json_out . size ( ) > 0 ) {
// we have a non-empty output
// log first
String msg ( " " ) ;
msg . reserve ( 100 ) ;
json_out . printTo ( msg ) ;
AddLog_P2 ( LOG_LEVEL_INFO , PSTR ( " ZIG: Auto-responder: ZbSend { \" Device \" : \" 0x%04X \" "
" , \" Cluster \" : \" 0x%04X \" "
" , \" Endpoint \" :%d "
" , \" Response \" :%s} "
) ,
srcaddr , cluster , endpoint ,
msg . c_str ( ) ) ;
// send
const JsonVariant & json_out_v = json_out ;
ZbSendReportWrite ( json_out_v , srcaddr , 0 /* group */ , cluster , endpoint , 0 /* manuf */ , ZCL_READ_ATTRIBUTES_RESPONSE ) ;
}
}
2019-09-29 14:38:26 +01:00
# endif // USE_ZIGBEE