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
/*********************************************************************************************\
* Parsers for incoming EZSP messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// EZSP: received ASH RSTACK frame, indicating that the MCU finished boot
int32_t Z_EZSP_RSTACK ( uint8_t reset_code ) {
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 ) ;
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
XdrvRulesProcess ( ) ;
}
// EZSP: received ASH ERROR frame, indicating that the MCU finished boot
int32_t Z_EZSP_ERROR ( uint8_t error_code ) {
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 ) ;
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
XdrvRulesProcess ( ) ;
}
/*********************************************************************************************\
* Default resolver
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int32_t Z_Recv_Default ( int32_t res , const class SBuffer & buf ) {
// Default message handler for new messages
if ( zigbee . init_phase ) {
// if still during initialization phase, ignore any unexpected message
return - 1 ; // ignore message
} else {
// TODO
// for (uint32_t i = 0; i < sizeof(Z_DispatchTable)/sizeof(Z_Dispatcher); i++) {
// if (Z_ReceiveMatchPrefix(buf, Z_DispatchTable[i].match)) {
// (*Z_DispatchTable[i].func)(res, buf);
// }
// }
return - 1 ;
}
}
int32_t Z_ReadAPSUnicastMessage ( int32_t res , class SBuffer & buf ) {
// Called when receiving a response from getConfigurationValue
// Value is in bytes 2+3
uint16_t value = buf . get16 ( 2 ) ;
return res ;
}
# endif // USE_ZIGBEE_EZSP
2020-03-23 21:46:26 +00:00
/*********************************************************************************************\
* Parsers for incoming ZNP messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
//
// Handle a "Receive Device Info" incoming message
//
2019-09-29 14:38:26 +01:00
int32_t Z_ReceiveDeviceInfo ( int32_t res , class SBuffer & buf ) {
// 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 ;
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 ( " ] " ) ) ;
}
ResponseJsonEnd ( ) ; // append '}'
ResponseJsonEnd ( ) ; // append '}'
2019-11-09 08:25:15 +00:00
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
2019-09-29 14:38:26 +01:00
XdrvRulesProcess ( ) ;
return res ;
}
int32_t Z_CheckNVWrite ( int32_t res , class SBuffer & buf ) {
// 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
}
}
2019-10-13 11:56:52 +01:00
int32_t Z_Reboot ( int32_t res , class SBuffer & buf ) {
// 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 ) ;
2019-11-09 08:25:15 +00:00
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
2019-10-13 11:56:52 +01:00
XdrvRulesProcess ( ) ;
if ( ( 0x02 = = major_rel ) & & ( 0x06 = = minor_rel ) ) {
return 0 ; // version 2.6.x is ok
} else {
return ZIGBEE_LABEL_UNSUPPORTED_VERSION ; // abort
}
}
2019-09-29 14:38:26 +01:00
int32_t Z_ReceiveCheckVersion ( int32_t res , class SBuffer & buf ) {
// 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 ) ;
2019-11-09 08:25:15 +00:00
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
2019-09-29 14:38:26 +01:00
XdrvRulesProcess ( ) ;
if ( ( 0x02 = = major_rel ) & & ( 0x06 = = minor_rel ) ) {
return 0 ; // version 2.6.x is ok
} else {
return ZIGBEE_LABEL_UNSUPPORTED_VERSION ; // abort
}
}
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
//
2019-09-29 14:38:26 +01:00
int32_t Z_ReceivePermitJoinStatus ( int32_t res , const class SBuffer & buf ) {
// 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 ( " \" }} " ) ) ;
2019-11-09 08:25:15 +00:00
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_STATE ) ) ;
2019-09-29 14:38:26 +01:00
XdrvRulesProcess ( ) ;
return - 1 ;
}
int32_t Z_ReceiveNodeDesc ( int32_t res , const class SBuffer & buf ) {
// 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
) ;
2019-10-06 11:40:58 +01:00
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
2019-09-29 14:38:26 +01:00
XdrvRulesProcess ( ) ;
}
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
Z_ShortAddress srcAddr = buf . get16 ( 2 ) ;
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 ) ;
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 ( " ]}} " ) ) ;
2019-10-06 11:40:58 +01:00
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
2019-09-29 14:38:26 +01:00
XdrvRulesProcess ( ) ;
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-02-23 15:46:00 +00:00
int32_t Z_ReceiveIEEEAddr ( int32_t res , const class SBuffer & buf ) {
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);
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
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
XdrvRulesProcess ( ) ;
}
return - 1 ;
}
2020-03-14 13:17:30 +00:00
//
// Report any AF_DATA_CONFIRM message
// Ex: {"ZbConfirm":{"Endpoint":1,"Status":0,"StatusMessage":"SUCCESS"}}
//
int32_t Z_DataConfirm ( int32_t res , const class SBuffer & buf ) {
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-03-14 13:17:30 +00:00
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
XdrvRulesProcess ( ) ;
}
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-05-11 20:16:17 +01:00
int32_t Z_ReceiveStateChange ( int32_t res , const class SBuffer & buf ) {
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
) ;
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
XdrvRulesProcess ( ) ;
}
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 ) {
Z_ShortAddress srcAddr = buf . get16 ( 2 ) ;
Z_ShortAddress nwkAddr = buf . get16 ( 4 ) ;
Z_IEEEAddress ieeeAddr = buf . get64 ( 6 ) ;
uint8_t capabilities = buf . get8 ( 14 ) ;
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
2019-10-06 11:40:58 +01:00
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
2019-09-29 14:38:26 +01:00
XdrvRulesProcess ( ) ;
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
//
2019-12-23 15:53:54 +00:00
int32_t Z_ReceiveTCDevInd ( int32_t res , const class SBuffer & buf ) {
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
) ;
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
XdrvRulesProcess ( ) ;
return - 1 ;
}
2020-03-23 21:46:26 +00:00
//
// Handle Bind Rsp incoming message
//
int32_t Z_BindRsp ( int32_t res , const class SBuffer & buf ) {
Z_ShortAddress nwkAddr = buf . get16 ( 2 ) ;
uint8_t status = buf . get8 ( 4 ) ;
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 \" "
" }} " ) , status , getZigbeeStatusMessage ( status ) . c_str ( ) ) ;
2020-03-23 21:46:26 +00:00
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
XdrvRulesProcess ( ) ;
return - 1 ;
}
2020-03-30 18:23:06 +01:00
2020-03-26 19:58:59 +00:00
//
// Handle Unbind Rsp incoming message
//
int32_t Z_UnbindRsp ( int32_t res , const class SBuffer & buf ) {
Z_ShortAddress nwkAddr = buf . get16 ( 2 ) ;
uint8_t status = buf . get8 ( 4 ) ;
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-05-17 17:33:42 +01:00
ResponseAppend_P ( PSTR ( " , \" " D_JSON_ZIGBEE_STATUS_MSG " \" : \" %s \" "
" }} " ) , status , getZigbeeStatusMessage ( status ) . c_str ( ) ) ;
2020-03-26 19:58:59 +00:00
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEEZCL_RECEIVED ) ) ;
XdrvRulesProcess ( ) ;
return - 1 ;
}
2020-03-30 18:23:06 +01:00
//
// Handle MgMt Bind Rsp incoming message
//
int32_t Z_MgmtBindRsp ( int32_t res , const class SBuffer & buf ) {
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 ) ;
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 "
" , \" Bindings \" :[ "
) , status , getZigbeeStatusMessage ( status ) . c_str ( ) , bind_total ) ;
uint32_t idx = 8 ;
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 {
//AddLog_P2(LOG_LEVEL_INFO, PSTR("Z_MgmtBindRsp unknwon address mode %d"), addrmode);
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 ( " ]}} " ) ) ;
MqttPublishPrefixTopic_P ( RESULT_OR_TELE , PSTR ( D_JSON_ZIGBEE_BIND_STATE ) ) ;
XdrvRulesProcess ( ) ;
return - 1 ;
}
2020-03-23 21:46:26 +00:00
2020-06-16 19:01:14 +01:00
# ifdef USE_ZIGBEE_ZNP
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 ) {
uint8_t IEEEAddrReq [ ] = { Z_SREQ | Z_ZDO , ZDO_IEEE_ADDR_REQ , Z_B0 ( shortaddr ) , Z_B1 ( shortaddr ) , 0x00 , 0x00 } ;
ZigbeeZNPSend ( IEEEAddrReq , sizeof ( IEEEAddrReq ) ) ;
}
//
// Send ACTIVE_EP_REQ to collect active endpoints for this address
//
void Z_SendActiveEpReq ( uint16_t shortaddr ) {
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 ) ) ;
}
//
// 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 ) ;
uint8_t AFInfoReq [ ] = { Z_SREQ | Z_AF , AF_DATA_REQUEST , Z_B0 ( shortaddr ) , Z_B1 ( shortaddr ) , endpoint ,
0x01 , 0x00 , 0x00 , transacid , 0x30 , 0x1E , 3 + 2 * sizeof ( uint16_t ) ,
0x00 , transacid , ZCL_READ_ATTRIBUTES , 0x04 , 0x00 , 0x05 , 0x00
} ;
ZigbeeZNPSend ( AFInfoReq , sizeof ( AFInfoReq ) ) ;
}
2020-06-16 19:01:14 +01:00
# endif // USE_ZIGBEE_ZNP
# ifdef USE_ZIGBEE_EZSP
/*********************************************************************************************\
* Send specific EZS ¨ messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-06-19 19:54:37 +01:00
//
// Callback for loading Zigbee configuration from Flash, called by the state machine
//
int32_t Z_Reset_Device ( uint8_t value ) {
// TODO - GPIO is hardwired to GPIO4
digitalWrite ( 4 , value ? HIGH : LOW ) ;
return 0 ; // continue
}
2020-06-16 19:01:14 +01:00
//
// Send ZDO_IEEE_ADDR_REQ request to get IEEE long address
//
void Z_SendIEEEAddrReq ( uint16_t shortaddr ) {
}
//
// Send ACTIVE_EP_REQ to collect active endpoints for this address
//
void Z_SendActiveEpReq ( uint16_t shortaddr ) {
}
//
// Send AF Info Request
//
void Z_SendAFInfoRequest ( uint16_t shortaddr ) {
}
# endif // USE_ZIGBEE_EZSP
2020-03-23 21:46:26 +00:00
/*********************************************************************************************\
* Callbacks
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-12-22 16:47:45 +00:00
// 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
2020-04-08 14:20:04 +01:00
void Z_AqaraOccupancy ( uint16_t shortaddr , uint16_t cluster , uint8_t endpoint , const JsonObject & json ) {
2020-03-23 21:46:26 +00:00
static const uint32_t OCCUPANCY_TIMEOUT = 90 * 1000 ; // 90 s
2019-12-22 16:47:45 +00:00
// Read OCCUPANCY value if any
2020-05-24 08:57:11 +01:00
const JsonVariant & val_endpoint = GetCaseInsensitive ( json , PSTR ( OCCUPANCY ) ) ;
2019-12-22 16:47:45 +00:00
if ( nullptr ! = & val_endpoint ) {
uint32_t occupancy = strToUInt ( val_endpoint ) ;
if ( occupancy ) {
2020-04-17 11:27:36 +01:00
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 ) ;
2019-12-22 16:47:45 +00:00
}
}
}
// Publish the received values once they have been coalesced
2020-03-14 13:17:30 +00:00
int32_t Z_PublishAttributes ( uint16_t shortaddr , uint16_t groupaddr , uint16_t cluster , uint8_t endpoint , uint32_t value ) {
2019-12-22 16:47:45 +00:00
const JsonObject * json = zigbee_devices . jsonGet ( shortaddr ) ;
if ( json = = nullptr ) { return 0 ; } // don't crash if not found
2020-01-17 23:02:01 +00:00
zigbee_devices . jsonPublishFlush ( shortaddr ) ;
2019-12-23 15:53:54 +00:00
return 1 ;
2019-12-22 16:47:45 +00:00
}
2020-03-23 21:46:26 +00:00
/*********************************************************************************************\
* Global dispatcher for incoming messages
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-09-29 14:38:26 +01:00
int32_t Z_ReceiveAfIncomingMessage ( int32_t res , const class SBuffer & buf ) {
uint16_t groupid = buf . get16 ( 2 ) ;
uint16_t clusterid = buf . get16 ( 4 ) ;
2020-05-17 17:33:42 +01:00
uint16_t srcaddr = buf . get16 ( 6 ) ;
2019-09-29 14:38:26 +01:00
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 ) ;
2019-12-22 16:47:45 +00:00
bool defer_attributes = false ; // do we defer attributes reporting to coalesce
2019-12-15 18:39:27 +00:00
ZCLFrame zcl_received = ZCLFrame : : parseRawFrame ( buf , 19 , buf . get8 ( 18 ) , clusterid , groupid ,
srcaddr ,
srcendpoint , dstendpoint , wasbroadcast ,
linkquality , securityuse , seqnumber ,
timestamp ) ;
zcl_received . log ( ) ;
2019-09-29 14:38:26 +01:00
char shortaddr [ 8 ] ;
snprintf_P ( shortaddr , sizeof ( shortaddr ) , PSTR ( " 0x%04X " ) , srcaddr ) ;
DynamicJsonBuffer jsonBuffer ;
2020-01-19 21:59:02 +00:00
JsonObject & json = jsonBuffer . createObject ( ) ;
2020-03-14 13:17:30 +00: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-03-01 10:25:59 +00:00
} else {
// 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
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
}
2019-09-29 14:38:26 +01:00
return - 1 ;
}
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 {
const uint8_t * match ;
ZB_RecvMsgFunc func ;
} Z_Dispatcher ;
2020-06-16 19:01:14 +01:00
# ifdef USE_ZIGBEE_ZNP
2020-03-23 21:46:26 +00:00
// Ffilters based on ZNP frames
ZBM ( AREQ_AF_DATA_CONFIRM , Z_AREQ | Z_AF , AF_DATA_CONFIRM ) // 4480
ZBM ( AREQ_AF_INCOMING_MESSAGE , Z_AREQ | Z_AF , AF_INCOMING_MSG ) // 4481
2020-05-11 20:16:17 +01:00
// ZBM(AREQ_STATE_CHANGE_IND, Z_AREQ | Z_ZDO, ZDO_STATE_CHANGE_IND) // 45C0
2020-03-23 21:46:26 +00:00
ZBM ( AREQ_END_DEVICE_ANNCE_IND , Z_AREQ | Z_ZDO , ZDO_END_DEVICE_ANNCE_IND ) // 45C1
ZBM ( AREQ_END_DEVICE_TC_DEV_IND , Z_AREQ | Z_ZDO , ZDO_TC_DEV_IND ) // 45CA
ZBM ( AREQ_PERMITJOIN_OPEN_XX , Z_AREQ | Z_ZDO , ZDO_PERMIT_JOIN_IND ) // 45CB
ZBM ( AREQ_ZDO_ACTIVEEPRSP , Z_AREQ | Z_ZDO , ZDO_ACTIVE_EP_RSP ) // 4585
ZBM ( AREQ_ZDO_SIMPLEDESCRSP , Z_AREQ | Z_ZDO , ZDO_SIMPLE_DESC_RSP ) // 4584
ZBM ( AREQ_ZDO_IEEE_ADDR_RSP , Z_AREQ | Z_ZDO , ZDO_IEEE_ADDR_RSP ) // 4581
ZBM ( AREQ_ZDO_BIND_RSP , Z_AREQ | Z_ZDO , ZDO_BIND_RSP ) // 45A1
2020-03-26 19:58:59 +00:00
ZBM ( AREQ_ZDO_UNBIND_RSP , Z_AREQ | Z_ZDO , ZDO_UNBIND_RSP ) // 45A2
2020-03-30 18:23:06 +01:00
ZBM ( AREQ_ZDO_MGMT_BIND_RSP , Z_AREQ | Z_ZDO , ZDO_MGMT_BIND_RSP ) // 45B3
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-03-14 13:17:30 +00:00
{ AREQ_AF_DATA_CONFIRM , & Z_DataConfirm } ,
2019-10-06 11:40:58 +01:00
{ AREQ_AF_INCOMING_MESSAGE , & Z_ReceiveAfIncomingMessage } ,
2020-05-11 20:16:17 +01:00
// { AREQ_STATE_CHANGE_IND, &Z_ReceiveStateChange },
2019-10-06 11:40:58 +01:00
{ AREQ_END_DEVICE_ANNCE_IND , & Z_ReceiveEndDeviceAnnonce } ,
2019-12-23 15:53:54 +00:00
{ AREQ_END_DEVICE_TC_DEV_IND , & Z_ReceiveTCDevInd } ,
2019-10-06 11:40:58 +01:00
{ AREQ_PERMITJOIN_OPEN_XX , & Z_ReceivePermitJoinStatus } ,
{ AREQ_ZDO_NODEDESCRSP , & Z_ReceiveNodeDesc } ,
{ AREQ_ZDO_ACTIVEEPRSP , & Z_ReceiveActiveEp } ,
2020-02-23 15:46:00 +00:00
{ AREQ_ZDO_IEEE_ADDR_RSP , & Z_ReceiveIEEEAddr } ,
2020-03-01 10:25:59 +00:00
{ AREQ_ZDO_BIND_RSP , & Z_BindRsp } ,
2020-03-26 19:58:59 +00:00
{ AREQ_ZDO_UNBIND_RSP , & Z_UnbindRsp } ,
2020-03-30 18:23:06 +01:00
{ AREQ_ZDO_MGMT_BIND_RSP , & Z_MgmtBindRsp } ,
2019-10-06 11:40:58 +01:00
} ;
2020-03-23 21:46:26 +00:00
/*********************************************************************************************\
* Default resolver
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-09-29 14:38:26 +01:00
int32_t Z_Recv_Default ( int32_t res , const class SBuffer & buf ) {
// Default message handler for new messages
if ( zigbee . init_phase ) {
// if still during initialization phase, ignore any unexpected message
return - 1 ; // ignore message
} else {
2019-10-06 11:40:58 +01:00
for ( uint32_t i = 0 ; i < sizeof ( Z_DispatchTable ) / sizeof ( Z_Dispatcher ) ; i + + ) {
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