Files
ESPEasy/src/_C009.cpp
T
TD-er 06e458ea04 [Controller] Fail gracefully when almost out of memory
One often reported issue is that the ESPEasy node (especially ESP8266 ones) will crash when WiFi is unstable.

This could be due to memory suddenly filling up by the controller queue.
When creating a new item for the controller queue, there was no `(std::nothrow)` included on the `new` calls, so if running out of memory this might have thrown a `std::bad_alloc` exception, which are not caught and thus ESPEasy might crash on it.
2024-05-23 15:20:14 +02:00

218 lines
7.5 KiB
C++

#include "src/Helpers/_CPlugin_Helper.h"
#ifdef USES_C009
#include "src/DataTypes/NodeTypeID.h"
#include "src/Helpers/StringProvider.h"
#include "src/CustomBuild/ESPEasy_buildinfo.h"
// #######################################################################################################
// ########################### Controller Plugin 009: FHEM HTTP ##########################################
// #######################################################################################################
/*******************************************************************************
* Copyright 2016-2017 dev0
* Contact: https://forum.fhem.de/index.php?action=profile;u=7465
* https://github.com/ddtlabs/
*
* Release notes:
- v1.0
- changed switch and dimmer setreading cmds
- v1.01
- added json content to http requests
- v1.02
- some optimizations as requested by mvdbro
- fixed JSON TaskDeviceValueDecimals handling
- ArduinoJson Library v5.6.4 required (as used by stable R120)
- parse for HTTP errors 400, 401
- moved on/off translation for Sensor_VType::SENSOR_TYPE_SWITCH/DIMMER to FHEM module
- v1.03
- changed http request from GET to POST (RFC conform)
- removed obsolete http get url code
- v1.04
- added build options and node_type_id to JSON/device
******************************************************************************/
# define CPLUGIN_009
# define CPLUGIN_ID_009 9
# define CPLUGIN_NAME_009 "FHEM HTTP"
bool CPlugin_009(CPlugin::Function function, struct EventStruct *event, String& string)
{
bool success = false;
switch (function)
{
case CPlugin::Function::CPLUGIN_PROTOCOL_ADD:
{
ProtocolStruct& proto = getProtocolStruct(event->idx); // = CPLUGIN_ID_009;
proto.usesMQTT = false;
proto.usesTemplate = false;
proto.usesAccount = true;
proto.usesPassword = true;
proto.usesExtCreds = true;
proto.usesID = false;
proto.defaultPort = 8383;
break;
}
case CPlugin::Function::CPLUGIN_GET_DEVICENAME:
{
string = F(CPLUGIN_NAME_009);
break;
}
case CPlugin::Function::CPLUGIN_INIT:
{
success = init_c009_delay_queue(event->ControllerIndex);
break;
}
case CPlugin::Function::CPLUGIN_EXIT:
{
exit_c009_delay_queue();
break;
}
case CPlugin::Function::CPLUGIN_PROTOCOL_SEND:
{
if (C009_DelayHandler != nullptr) {
if (C009_DelayHandler->queueFull(event->ControllerIndex)) {
break;
}
std::unique_ptr<C009_queue_element> element(new (std::nothrow) C009_queue_element(event));
success = C009_DelayHandler->addToQueue(std::move(element));
Scheduler.scheduleNextDelayQueue(SchedulerIntervalTimer_e::TIMER_C009_DELAY_QUEUE, C009_DelayHandler->getNextScheduleTime());
}
break;
}
case CPlugin::Function::CPLUGIN_FLUSH:
{
process_c009_delay_queue();
delay(0);
break;
}
default:
break;
}
return success;
}
/*********************************************************************************************\
* FHEM HTTP request
\*********************************************************************************************/
// Uncrustify may change this into multi line, which will result in failed builds
// *INDENT-OFF*
bool do_process_c009_delay_queue(int controller_number, const Queue_element_base& element_base, ControllerSettingsStruct& ControllerSettings) {
const C009_queue_element& element = static_cast<const C009_queue_element&>(element_base);
// *INDENT-ON*
String jsonString;
// Make an educated guess on the actual length, based on earlier requests.
static size_t expectedJsonLength = 100;
{
// Reserve on the heap with most space
if (!reserve_special(jsonString, expectedJsonLength)) {
// Not enough free memory
return false;
}
}
{
jsonString += '{';
{
jsonString += to_json_object_value(F("module"), F("ESPEasy"));
jsonString += ',';
jsonString += to_json_object_value(F("version"), F("1.04"));
// Create nested object "ESP" inside "data"
jsonString += ',';
jsonString += F("\"data\":{");
{
jsonString += F("\"ESP\":{");
{
// Create nested objects in "ESP":
jsonString += to_json_object_value(F("name"), Settings.getName());
jsonString += ',';
jsonString += to_json_object_value(F("unit"), static_cast<int>(Settings.Unit));
jsonString += ',';
jsonString += to_json_object_value(F("version"), static_cast<int>(Settings.Version));
jsonString += ',';
jsonString += to_json_object_value(F("build"), static_cast<int>(Settings.Build));
jsonString += ',';
jsonString += to_json_object_value(F("build_notes"), F(BUILD_NOTES));
jsonString += ',';
jsonString += to_json_object_value(F("build_git"), getValue(LabelType::GIT_BUILD));
jsonString += ',';
jsonString += to_json_object_value(F("node_type_id"), static_cast<int>(NODE_TYPE_ID));
jsonString += ',';
jsonString += to_json_object_value(F("sleep"), static_cast<int>(Settings.deepSleep_wakeTime));
// embed IP, important if there is NAT/PAT
// char ipStr[20];
// IPAddress ip = NetworkLocalIP();
// sprintf_P(ipStr, PSTR("%u.%u.%u.%u"), ip[0], ip[1], ip[2], ip[3]);
jsonString += ',';
jsonString += to_json_object_value(F("ip"), formatIP(NetworkLocalIP()));
}
jsonString += '}'; // End "ESP"
jsonString += ',';
// Create nested object "SENSOR" json object inside "data"
jsonString += F("\"SENSOR\":{");
{
// char itemNames[valueCount][2];
for (uint8_t x = 0; x < element.valueCount; x++)
{
// Each sensor value get an own object (0..n)
// sprintf(itemNames[x],"%d",x);
if (x != 0) {
jsonString += ',';
}
jsonString += '"';
jsonString += x;
jsonString += F("\":{");
{
jsonString += to_json_object_value(F("deviceName"), getTaskDeviceName(element._taskIndex));
jsonString += ',';
jsonString += to_json_object_value(F("valueName"), getTaskValueName(element._taskIndex, x));
jsonString += ',';
jsonString += to_json_object_value(F("type"), static_cast<int>(element.sensorType));
jsonString += ',';
jsonString += to_json_object_value(F("value"), element.txt[x]);
}
jsonString += '}'; // End "sensor value N"
}
}
jsonString += '}'; // End "SENSOR"
}
jsonString += '}'; // End "data"
}
jsonString += '}'; // End JSON structure
}
if (expectedJsonLength < jsonString.length()) {
expectedJsonLength = jsonString.length();
}
// addLog(LOG_LEVEL_INFO, F("C009 Test JSON:"));
// addLog(LOG_LEVEL_INFO, jsonString);
int httpCode = -1;
send_via_http(
controller_number,
ControllerSettings,
element._controller_idx,
F("/ESPEasy"),
F("POST"),
EMPTY_STRING,
jsonString,
httpCode);
return (httpCode >= 100) && (httpCode < 300);
}
#endif // ifdef USES_C009