Skip to content

Commit

Permalink
Zigbee loads device data early before MCU startup (#21917)
Browse files Browse the repository at this point in the history
  • Loading branch information
s-hadinger authored Aug 6, 2024
1 parent 4f035d1 commit f88a635
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 52 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ All notable changes to this project will be documented in this file.
- ESP32 platform update from 2024.07.11 to 2024.08.10 (#21893)
- ESP32 Framework (Arduino Core) from v3.0.2 to v3.0.4 (#21893)
- Refactored Analog driver to better support multiple channels
- Zigbee loads device data early before MCU startup

### Fixed
- Berry `light.get` for separate RGB/CT (#21818)
Expand Down
2 changes: 1 addition & 1 deletion tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_1_headers.ino
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public:
#endif // USE_ZIGBEE_EEPROM
{}

bool active = true; // is Zigbee active for this device, i.e. GPIOs configured
bool active = false; // is Zigbee active for this device, i.e. GPIOs configured
bool state_machine = false; // the state machine is running
bool state_waiting = false; // the state machine is waiting for external event or timeout
bool state_no_timeout = false; // the current wait loop does not generate a timeout but only continues running
Expand Down
54 changes: 43 additions & 11 deletions tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_7_0_statemachine.ino
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,27 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT)
ZI_ON_RECV_UNEXPECTED(&ZNP_Recv_Default)

ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "Loading Zigbee data")
ZI_CALL(&Z_Prepare_Storage, 0)
ZI_CALL(&Z_Load_Devices, 0)
ZI_CALL(&Z_Load_Data, 0)
ZI_CALL(&Z_Set_Save_Data_Timer, 0)
ZI_CALL(&Z_ZbAutoload, 0)
#ifndef USE_ZIGBEE_DEBUG
ZI_WAIT(15500) // wait for 15 seconds for Tasmota to stabilize
#else
ZI_WAIT(30000) // wait for cumulated 5 minutes
ZI_WAIT(30000)
ZI_WAIT(30000)
ZI_WAIT(30000)
ZI_WAIT(30000)
ZI_WAIT(30000)
ZI_WAIT(30000)
ZI_WAIT(30000)
ZI_WAIT(30000)
ZI_WAIT(30000)
#endif

//ZI_MQTT_STATE(ZIGBEE_STATUS_BOOT, "Booting")
ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "rebooting ZNP device")
Expand Down Expand Up @@ -532,11 +552,6 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
// Correctly configured and running, enable all Tasmota features
// ======================================================================
ZI_LABEL(ZIGBEE_LABEL_READY)
ZI_CALL(&Z_Prepare_Storage, 0)
ZI_CALL(&Z_Load_Devices, 0)
ZI_CALL(&Z_Load_Data, 0)
ZI_CALL(&Z_Set_Save_Data_Timer, 0)
ZI_CALL(&Z_ZbAutoload, 0)
ZI_MQTT_STATE(ZIGBEE_STATUS_OK, kStarted)
ZI_LOG(LOG_LEVEL_INFO, kZigbeeStarted)
ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages
Expand Down Expand Up @@ -881,7 +896,27 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
ZI_ON_TIMEOUT_GOTO(ZIGBEE_LABEL_ABORT)
ZI_ON_RECV_UNEXPECTED(&EZ_Recv_Default)

ZI_LOG(LOG_LEVEL_INFO, D_LOG_ZIGBEE "Loading Zigbee data")
ZI_CALL(&Z_Prepare_Storage, 0)
ZI_CALL(&Z_Load_Devices, 0)
ZI_CALL(&Z_Load_Data, 0)
ZI_CALL(&Z_Set_Save_Data_Timer, 0)
ZI_CALL(&Z_ZbAutoload, 0)
#ifndef USE_ZIGBEE_DEBUG
ZI_WAIT(15500) // wait for 15 seconds for Tasmota to stabilize
#else
ZI_WAIT(30000) // wait for cumulated 5 minutes
ZI_WAIT(30000)
ZI_WAIT(30000)
ZI_WAIT(30000)
ZI_WAIT(30000)
ZI_WAIT(30000)
ZI_WAIT(30000)
ZI_WAIT(30000)
ZI_WAIT(30000)
ZI_WAIT(30000)
#endif

// Hardware reset
ZI_LOG(LOG_LEVEL_INFO, kResettingDevice) // Log Debug: resetting EZSP device
Expand Down Expand Up @@ -978,12 +1013,7 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
ZI_LOG(LOG_LEVEL_INFO, kZigbeeGroup0)
ZI_SEND(ZBS_SET_MCAST_ENTRY) ZI_WAIT_RECV(2500, ZBR_SET_MCAST_ENTRY)

// ZI_LABEL(ZIGBEE_LABEL_READY)
ZI_CALL(&Z_Prepare_Storage, 0)
ZI_CALL(&Z_Load_Devices, 0)
ZI_CALL(&Z_Load_Data, 0)
ZI_CALL(&Z_Set_Save_Data_Timer, 0)
ZI_CALL(&Z_ZbAutoload, 0)
ZI_LABEL(ZIGBEE_LABEL_READY)
ZI_MQTT_STATE(ZIGBEE_STATUS_OK, kStarted)
ZI_LOG(LOG_LEVEL_INFO, kZigbeeStarted)
ZI_CALL(&Z_State_Ready, 1) // Now accept incoming messages
Expand Down Expand Up @@ -1088,6 +1118,7 @@ void ZigbeeStateMachine_Run(void) {
}
if (zigbee.pc < 0) {
zigbee.state_machine = false;
zigbee.active = false;
return;
}

Expand Down Expand Up @@ -1133,6 +1164,7 @@ void ZigbeeStateMachine_Run(void) {
zigbee.state_machine = false;
if (cur_d8) {
AddLog(LOG_LEVEL_ERROR, PSTR(D_LOG_ZIGBEE "Stopping (%d)"), cur_d8);
zigbee.active = false;
}
break;
case ZGB_INSTR_CALL:
Expand Down
58 changes: 21 additions & 37 deletions tasmota/tasmota_xdrv_driver/xdrv_23_zigbee_A_impl.ino
Original file line number Diff line number Diff line change
Expand Up @@ -1172,8 +1172,6 @@ void CmndZbName(void) {
//
// Where <device_id> can be: short_addr, long_addr, device_index, friendly_name

if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }

// check if parameters contain a comma ','
char *p = XdrvMailbox.data;
char *device_id = strsep(&p, ","); // zigbee identifier
Expand Down Expand Up @@ -1214,8 +1212,6 @@ void CmndZbModelId(void) {
//
// Where <device_id> can be: short_addr, long_addr, device_index, friendly_name

if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }

// check if parameters contain a comma ','
char *p;
strtok_r(XdrvMailbox.data, ",", &p);
Expand All @@ -1241,8 +1237,6 @@ void CmndZbLight(void) {
//
// Where <device_id> can be: short_addr, long_addr, device_index, friendly_name

if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }

// check if parameters contain a comma ','
char *p = XdrvMailbox.data;
char *device_id = strsep(&p, ","); // zigbee identifier
Expand Down Expand Up @@ -1287,8 +1281,6 @@ void CmndZbOccupancy(void) {
// 0x8 = 120 s
// Where <device_id> can be: short_addr, long_addr, device_index, friendly_name

if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }

// check if parameters contain a comma ','
char *p;
strtok_r(XdrvMailbox.data, ",", &p);
Expand Down Expand Up @@ -1320,7 +1312,6 @@ void CmndZbOccupancy(void) {
// Remove an old Zigbee device from the list of known devices, use ZigbeeStatus to know all registered devices
//
void CmndZbForget(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, nullptr, XdrvMailbox.payload); // in case of short_addr, it must be already registered
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }

Expand All @@ -1342,7 +1333,6 @@ void CmndZbInfo_inner(const Z_Device & device) {
device.jsonPublishAttrList(PSTR(D_JSON_ZIGBEE_INFO), attr_list); // publish as ZbReceived
}
void CmndZbInfo(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
TrimSpace(XdrvMailbox.data);

if (strlen(XdrvMailbox.data) == 0) {
Expand All @@ -1369,7 +1359,6 @@ void CmndZbInfo(void) {
// Save Zigbee information to flash
//
void CmndZbSave(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
switch (XdrvMailbox.payload) {
case 2: // save only data
hibernateAllData();
Expand Down Expand Up @@ -1578,7 +1567,6 @@ void CmndZbEmulation(void) {
// ZbRestore [{"Device":"0x5ADF","Name":"Petite_Lampe","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":["0x01","0xF2"]}]
// ZbRestore {"Device":"0x5ADF","Name":"Petite_Lampe","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":["0x01","0xF2"]}
void CmndZbRestore(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
TrimSpace(XdrvMailbox.data);

if (strlen(XdrvMailbox.data) == 0) {
Expand Down Expand Up @@ -1795,34 +1783,31 @@ void ZigbeePermitJoinUpdate(void) {
// 1.b. `ZbStatus Room` - Show single device shortaddr and name `{"ZbStatus1":[{"Device":"0x868E","Name":"Room"}]}`
// 2. `ZbStatus2 Room` - Show detailed information of device `{"ZbStatus2":[{"Device":"0x868E","Name":"Room","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":[1,242],"Config":["L01.2","O01"]}]}`
void CmndZbStatus(void) {
if (ZigbeeSerial) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
String dump;
TrimSpace(XdrvMailbox.data);
String dump;

if (0 == XdrvMailbox.index) { // Case 0
dump = zigbee_devices.dumpCoordinator();
if (0 == XdrvMailbox.index) { // Case 0
dump = zigbee_devices.dumpCoordinator();
} else {
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, nullptr, XdrvMailbox.payload);
if (XdrvMailbox.data_len > 0) {
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
// case 1.b and 2.
dump = zigbee_devices.dumpDevice(XdrvMailbox.index, device);
} else {
Z_Device & device = zigbee_devices.parseDeviceFromName(XdrvMailbox.data, nullptr, nullptr, XdrvMailbox.payload);
if (XdrvMailbox.data_len > 0) {
if (!device.valid()) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
// case 1.b and 2.
dump = zigbee_devices.dumpDevice(XdrvMailbox.index, device);
} else {
if (XdrvMailbox.index >= 2) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
// case 1.a
dump = zigbee_devices.dumpDevice(XdrvMailbox.index, device_unk);
}
if (XdrvMailbox.index >= 2) { ResponseCmndChar_P(PSTR(D_ZIGBEE_UNKNOWN_DEVICE)); return; }
// case 1.a
dump = zigbee_devices.dumpDevice(XdrvMailbox.index, device_unk);
}

Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str());
}

Response_P(PSTR("{\"%s%d\":%s}"), XdrvMailbox.command, XdrvMailbox.index, dump.c_str());
}

//
// Command `ZbData`
//
void CmndZbData(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
TrimSpace(XdrvMailbox.data);

if (strlen(XdrvMailbox.data) == 0) {
Expand Down Expand Up @@ -1867,7 +1852,6 @@ void CmndZbConfig(void) {
uint64_t zb_precfgkey_h = Settings->zb_precfgkey_h;
int8_t zb_txradio_dbm = Settings->zb_txradio_dbm;

// if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
TrimSpace(XdrvMailbox.data);
if (strlen(XdrvMailbox.data) > 0) {
JsonParser parser(XdrvMailbox.data);
Expand Down Expand Up @@ -2224,10 +2208,10 @@ void ZigbeeShow(bool json)
if (json) {
return;
#ifdef USE_WEBSERVER
} else {
} else {
UnishoxStrings msg(ZB_WEB);
uint32_t zigbee_num = zigbee_devices.devicesSize();
if (zigbee_num > 0) {
if ((zigbee_num > 0) && (!zigbee.init_phase)) { // don't displays devices on UI if still in initialization phase
if (zigbee_num > 255) { zigbee_num = 255; }

WSContentSend_P(msg[ZB_WEB_CSS], WebColor(COL_TEXT));
Expand Down Expand Up @@ -2426,7 +2410,7 @@ void ZigbeeShow(bool json)
zigbee.major_rel, zigbee.minor_rel,
zigbee.maint_rel, zigbee.revision);
WSContentSend_P(HTTP_BTN_ZB_BUTTONS);
} else if (zigbee.state_machine) { // show buttons only if the state machine is still running. If not running anymore, it means aborted
} else {
uint32_t grey = WebColor(COL_FORM);
WSContentSend_P(HTTP_BTN_ZB_BUTTONS_DISABLED, grey, grey);
}
Expand Down Expand Up @@ -2485,6 +2469,9 @@ bool Xdrv23(uint32_t function) {
if (TasmotaGlobal.gpio_optiona.enable_ccloader) { return false; }

bool result = false;
if (!zigbee.active && (FUNC_PRE_INIT == function)) {
ZigbeeInit();
}

if (zigbee.active) {
switch (function) {
Expand Down Expand Up @@ -2521,9 +2508,6 @@ bool Xdrv23(uint32_t function) {
WebServer_on(PSTR("/zbr"), ZigbeeMapRefresh, HTTP_GET); // add web handler for Zigbee map refresh
break;
#endif // USE_WEBSERVER
case FUNC_PRE_INIT:
ZigbeeInit();
break;
case FUNC_COMMAND:
result = DecodeCommand(kZbCommands, ZigbeeCommand, kZbSynonyms);
break;
Expand Down
6 changes: 3 additions & 3 deletions tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_zigbee.ino
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ extern "C" {
int zc_started(struct bvm *vm) {
// return `nil` if `zigbee.active` is false (i.e. no GPIO configured)
// or aborted, `zigbee.init_phase` is `true` but `zigbee.state_machine` is `false`
if (!zigbee.active || (!zigbee.state_machine && zigbee.init_phase)) {
if (!zigbee.active) {
be_return_nil(vm);
}
be_pushbool(vm, !zigbee.init_phase);
Expand Down Expand Up @@ -117,7 +117,7 @@ extern "C" {
// implement item() and find()
int zc_item_or_find(struct bvm *vm, bbool raise_if_unknown) {
int32_t top = be_top(vm); // Get the number of arguments
if (zigbee.init_phase) {
if (!zigbee.active) {
be_raise(vm, "internal_error", "zigbee not started");
}
if (top >= 2 && (be_isint(vm, 2) || be_isstring(vm, 2))) {
Expand Down Expand Up @@ -189,7 +189,7 @@ extern "C" {

int zc_iter(bvm *vm);
int zc_iter(bvm *vm) {
if (zigbee.init_phase) {
if (!zigbee.active) {
be_raise(vm, "internal_error", "zigbee not started");
}
be_pushntvclosure(vm, zc_iter_closure, 1);
Expand Down

0 comments on commit f88a635

Please sign in to comment.