Merge remote-tracking branch 'kahrendt/json-bump-library' into integration

This commit is contained in:
J. Nick Koston 2025-07-14 14:33:42 -10:00
commit cf9130f906
No known key found for this signature in database
13 changed files with 36 additions and 27 deletions

View File

@ -83,8 +83,7 @@ void HttpRequestUpdate::update_task(void *params) {
container.reset(); // Release ownership of the container's shared_ptr container.reset(); // Release ownership of the container's shared_ptr
valid = json::parse_json(response, [this_update](JsonObject root) -> bool { valid = json::parse_json(response, [this_update](JsonObject root) -> bool {
if (!root["name"].is<const char *>() || !root["version"].is<const char *>() || if (!root["name"].is<const char *>() || !root["version"].is<const char *>() || !root["builds"].is<JsonArray>()) {
!root["builds"].is<const char *>()) {
ESP_LOGE(TAG, "Manifest does not contain required fields"); ESP_LOGE(TAG, "Manifest does not contain required fields");
return false; return false;
} }

View File

@ -1,7 +1,7 @@
#include "json_util.h" #include "json_util.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include <ArduinoJson/Memory/Allocator.hpp> // ArduinoJson::Allocator is included via ArduinoJson.h in json_util.h
namespace esphome { namespace esphome {
namespace json { namespace json {
@ -13,7 +13,12 @@ struct SpiRamAllocator : ArduinoJson::Allocator {
void *allocate(size_t size) override { return this->allocator_.allocate(size); } void *allocate(size_t size) override { return this->allocator_.allocate(size); }
void deallocate(void *pointer) override { void deallocate(void *pointer) override {
// RAMAllocator requires passing the size of the allocated space which don't know, so use free directly // ArduinoJson's Allocator interface doesn't provide the size parameter in deallocate.
// RAMAllocator::deallocate() requires the size, which we don't have access to here.
// RAMAllocator::deallocate implementation just calls free() regardless of whether
// the memory was allocated with heap_caps_malloc or malloc.
// This is safe because ESP-IDF's heap implementation internally tracks the memory region
// and routes free() to the appropriate heap.
free(pointer); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) free(pointer); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
} }
@ -26,18 +31,15 @@ struct SpiRamAllocator : ArduinoJson::Allocator {
}; };
std::string build_json(const json_build_t &f) { std::string build_json(const json_build_t &f) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
auto doc_allocator = SpiRamAllocator(); auto doc_allocator = SpiRamAllocator();
JsonDocument json_document(&doc_allocator); JsonDocument json_document(&doc_allocator);
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
if (json_document.overflowed()) { if (json_document.overflowed()) {
ESP_LOGE(TAG, "Could not allocate memory for JSON document!"); ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
return "{}"; return "{}";
} }
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
JsonObject root = json_document.to<JsonObject>(); JsonObject root = json_document.to<JsonObject>();
f(root); f(root);
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
if (json_document.overflowed()) { if (json_document.overflowed()) {
ESP_LOGE(TAG, "Could not allocate memory for JSON document!"); ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
return "{}"; return "{}";
@ -45,18 +47,17 @@ std::string build_json(const json_build_t &f) {
std::string output; std::string output;
serializeJson(json_document, output); serializeJson(json_document, output);
return output; return output;
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
} }
bool parse_json(const std::string &data, const json_parse_t &f) { bool parse_json(const std::string &data, const json_parse_t &f) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
auto doc_allocator = SpiRamAllocator(); auto doc_allocator = SpiRamAllocator();
JsonDocument json_document(&doc_allocator); JsonDocument json_document(&doc_allocator);
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
if (json_document.overflowed()) { if (json_document.overflowed()) {
ESP_LOGE(TAG, "Could not allocate memory for JSON document!"); ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
return false; return false;
} }
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
DeserializationError err = deserializeJson(json_document, data); DeserializationError err = deserializeJson(json_document, data);
JsonObject root = json_document.as<JsonObject>(); JsonObject root = json_document.as<JsonObject>();
@ -69,6 +70,7 @@ bool parse_json(const std::string &data, const json_parse_t &f) {
} }
ESP_LOGE(TAG, "Parse error: %s", err.c_str()); ESP_LOGE(TAG, "Parse error: %s", err.c_str());
return false; return false;
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
} }
} // namespace json } // namespace json

View File

@ -31,10 +31,12 @@ void MQTTButtonComponent::dump_config() {
} }
void MQTTButtonComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { void MQTTButtonComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
config.state_topic = false; config.state_topic = false;
if (!this->button_->get_device_class().empty()) if (!this->button_->get_device_class().empty()) {
root[MQTT_DEVICE_CLASS] = this->button_->get_device_class(); root[MQTT_DEVICE_CLASS] = this->button_->get_device_class();
}
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
} }
std::string MQTTButtonComponent::component_type() const { return "button"; } std::string MQTTButtonComponent::component_type() const { return "button"; }

View File

@ -92,10 +92,10 @@ void MQTTClientComponent::send_device_info_() {
std::string topic = "esphome/discover/"; std::string topic = "esphome/discover/";
topic.append(App.get_name()); topic.append(App.get_name());
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
this->publish_json( this->publish_json(
topic, topic,
[](JsonObject root) { [](JsonObject root) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
uint8_t index = 0; uint8_t index = 0;
for (auto &ip : network::get_ip_addresses()) { for (auto &ip : network::get_ip_addresses()) {
if (ip.is_set()) { if (ip.is_set()) {
@ -148,6 +148,7 @@ void MQTTClientComponent::send_device_info_() {
#endif #endif
}, },
2, this->discovery_info_.retain); 2, this->discovery_info_.retain);
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
} }
void MQTTClientComponent::dump_config() { void MQTTClientComponent::dump_config() {

View File

@ -14,7 +14,7 @@ static const char *const TAG = "mqtt.climate";
using namespace esphome::climate; using namespace esphome::climate;
void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
auto traits = this->device_->get_traits(); auto traits = this->device_->get_traits();
// current_temperature_topic // current_temperature_topic
if (traits.get_supports_current_temperature()) { if (traits.get_supports_current_temperature()) {
@ -29,7 +29,6 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
// mode_state_topic // mode_state_topic
root[MQTT_MODE_STATE_TOPIC] = this->get_mode_state_topic(); root[MQTT_MODE_STATE_TOPIC] = this->get_mode_state_topic();
// modes // modes
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
JsonArray modes = root[MQTT_MODES].to<JsonArray>(); JsonArray modes = root[MQTT_MODES].to<JsonArray>();
// sort array for nice UI in HA // sort array for nice UI in HA
if (traits.supports_mode(CLIMATE_MODE_AUTO)) if (traits.supports_mode(CLIMATE_MODE_AUTO))
@ -165,6 +164,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
config.state_topic = false; config.state_topic = false;
config.command_topic = false; config.command_topic = false;
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
} }
void MQTTClimateComponent::setup() { void MQTTClimateComponent::setup() {
auto traits = this->device_->get_traits(); auto traits = this->device_->get_traits();

View File

@ -70,10 +70,10 @@ bool MQTTComponent::send_discovery_() {
ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name().c_str()); ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name().c_str());
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return global_mqtt_client->publish_json( return global_mqtt_client->publish_json(
this->get_discovery_topic_(discovery_info), this->get_discovery_topic_(discovery_info),
[this](JsonObject root) { [this](JsonObject root) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
SendDiscoveryConfig config; SendDiscoveryConfig config;
config.state_topic = true; config.state_topic = true;
config.command_topic = true; config.command_topic = true;
@ -189,11 +189,11 @@ bool MQTTComponent::send_discovery_() {
device_info[MQTT_DEVICE_SUGGESTED_AREA] = node_area; device_info[MQTT_DEVICE_SUGGESTED_AREA] = node_area;
} }
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
device_info[MQTT_DEVICE_CONNECTIONS][0][0] = "mac"; device_info[MQTT_DEVICE_CONNECTIONS][0][0] = "mac";
device_info[MQTT_DEVICE_CONNECTIONS][0][1] = mac; device_info[MQTT_DEVICE_CONNECTIONS][0][1] = mac;
}, },
this->qos_, discovery_info.retain); this->qos_, discovery_info.retain);
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
} }
uint8_t MQTTComponent::get_qos() const { return this->qos_; } uint8_t MQTTComponent::get_qos() const { return this->qos_; }

View File

@ -26,7 +26,7 @@ void MQTTDateComponent::setup() {
if (root["month"].is<uint8_t>()) { if (root["month"].is<uint8_t>()) {
call.set_month(root["month"]); call.set_month(root["month"]);
} }
if (root["day"].is<uint16_t>()) { if (root["day"].is<uint8_t>()) {
call.set_day(root["day"]); call.set_day(root["day"]);
} }
call.perform(); call.perform();

View File

@ -29,13 +29,13 @@ void MQTTDateTimeComponent::setup() {
if (root["day"].is<uint8_t>()) { if (root["day"].is<uint8_t>()) {
call.set_day(root["day"]); call.set_day(root["day"]);
} }
if (root["hour"].is<uint16_t>()) { if (root["hour"].is<uint8_t>()) {
call.set_hour(root["hour"]); call.set_hour(root["hour"]);
} }
if (root["minute"].is<uint16_t>()) { if (root["minute"].is<uint8_t>()) {
call.set_minute(root["minute"]); call.set_minute(root["minute"]);
} }
if (root["second"].is<uint16_t>()) { if (root["second"].is<uint8_t>()) {
call.set_second(root["second"]); call.set_second(root["second"]);
} }
call.perform(); call.perform();

View File

@ -45,6 +45,7 @@ void MQTTJSONLightComponent::send_discovery(JsonObject root, mqtt::SendDiscovery
auto traits = this->state_->get_traits(); auto traits = this->state_->get_traits();
root[MQTT_COLOR_MODE] = true; root[MQTT_COLOR_MODE] = true;
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
JsonArray color_modes = root["supported_color_modes"].to<JsonArray>(); JsonArray color_modes = root["supported_color_modes"].to<JsonArray>();
if (traits.supports_color_mode(ColorMode::ON_OFF)) if (traits.supports_color_mode(ColorMode::ON_OFF))
color_modes.add("onoff"); color_modes.add("onoff");

View File

@ -38,9 +38,9 @@ std::string MQTTNumberComponent::component_type() const { return "number"; }
const EntityBase *MQTTNumberComponent::get_entity() const { return this->number_; } const EntityBase *MQTTNumberComponent::get_entity() const { return this->number_; }
void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
const auto &traits = number_->traits; const auto &traits = number_->traits;
// https://www.home-assistant.io/integrations/number.mqtt/ // https://www.home-assistant.io/integrations/number.mqtt/
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
root[MQTT_MIN] = traits.get_min_value(); root[MQTT_MIN] = traits.get_min_value();
root[MQTT_MAX] = traits.get_max_value(); root[MQTT_MAX] = traits.get_max_value();
root[MQTT_STEP] = traits.get_step(); root[MQTT_STEP] = traits.get_step();

View File

@ -33,9 +33,9 @@ std::string MQTTSelectComponent::component_type() const { return "select"; }
const EntityBase *MQTTSelectComponent::get_entity() const { return this->select_; } const EntityBase *MQTTSelectComponent::get_entity() const { return this->select_; }
void MQTTSelectComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { void MQTTSelectComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
const auto &traits = select_->traits; const auto &traits = select_->traits;
// https://www.home-assistant.io/integrations/select.mqtt/ // https://www.home-assistant.io/integrations/select.mqtt/
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
JsonArray options = root[MQTT_OPTIONS].to<JsonArray>(); JsonArray options = root[MQTT_OPTIONS].to<JsonArray>();
for (const auto &option : traits.get_options()) for (const auto &option : traits.get_options())
options.add(option); options.add(option);

View File

@ -20,13 +20,13 @@ MQTTTimeComponent::MQTTTimeComponent(TimeEntity *time) : time_(time) {}
void MQTTTimeComponent::setup() { void MQTTTimeComponent::setup() {
this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) { this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) {
auto call = this->time_->make_call(); auto call = this->time_->make_call();
if (root["hour"].is<uint16_t>()) { if (root["hour"].is<uint8_t>()) {
call.set_hour(root["hour"]); call.set_hour(root["hour"]);
} }
if (root["minute"].is<uint16_t>()) { if (root["minute"].is<uint8_t>()) {
call.set_minute(root["minute"]); call.set_minute(root["minute"]);
} }
if (root["second"].is<uint16_t>()) { if (root["second"].is<uint8_t>()) {
call.set_second(root["second"]); call.set_second(root["second"]);
} }
call.perform(); call.perform();

View File

@ -1322,6 +1322,7 @@ std::string WebServer::climate_all_json_generator(WebServer *web_server, void *s
return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL); return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL);
} }
std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) { std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return json::build_json([this, obj, start_config](JsonObject root) { return json::build_json([this, obj, start_config](JsonObject root) {
set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config); set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
const auto traits = obj->get_traits(); const auto traits = obj->get_traits();
@ -1407,6 +1408,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
root["state"] = root["target_temperature"]; root["state"] = root["target_temperature"];
} }
}); });
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
} }
#endif #endif
@ -1682,6 +1684,7 @@ std::string WebServer::update_all_json_generator(WebServer *web_server, void *so
return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE); return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE);
} }
std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) { std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return json::build_json([this, obj, start_config](JsonObject root) { return json::build_json([this, obj, start_config](JsonObject root) {
set_json_id(root, obj, "update-" + obj->get_object_id(), start_config); set_json_id(root, obj, "update-" + obj->get_object_id(), start_config);
root["value"] = obj->update_info.latest_version; root["value"] = obj->update_info.latest_version;
@ -1707,6 +1710,7 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c
this->add_sorting_info_(root, obj); this->add_sorting_info_(root, obj);
} }
}); });
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
} }
#endif #endif