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
valid = json::parse_json(response, [this_update](JsonObject root) -> bool {
if (!root["name"].is<const char *>() || !root["version"].is<const char *>() ||
!root["builds"].is<const char *>()) {
if (!root["name"].is<const char *>() || !root["version"].is<const char *>() || !root["builds"].is<JsonArray>()) {
ESP_LOGE(TAG, "Manifest does not contain required fields");
return false;
}

View File

@ -1,7 +1,7 @@
#include "json_util.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 json {
@ -13,7 +13,12 @@ struct SpiRamAllocator : ArduinoJson::Allocator {
void *allocate(size_t size) override { return this->allocator_.allocate(size); }
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)
}
@ -26,18 +31,15 @@ struct SpiRamAllocator : ArduinoJson::Allocator {
};
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();
JsonDocument json_document(&doc_allocator);
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
if (json_document.overflowed()) {
ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
return "{}";
}
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
JsonObject root = json_document.to<JsonObject>();
f(root);
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
if (json_document.overflowed()) {
ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
return "{}";
@ -45,18 +47,17 @@ std::string build_json(const json_build_t &f) {
std::string output;
serializeJson(json_document, output);
return output;
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
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();
JsonDocument json_document(&doc_allocator);
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
if (json_document.overflowed()) {
ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
return false;
}
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
DeserializationError err = deserializeJson(json_document, data);
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());
return false;
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
} // namespace json

View File

@ -31,11 +31,13 @@ void MQTTButtonComponent::dump_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;
if (!this->button_->get_device_class().empty())
if (!this->button_->get_device_class().empty()) {
root[MQTT_DEVICE_CLASS] = this->button_->get_device_class();
}
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
std::string MQTTButtonComponent::component_type() const { return "button"; }
const EntityBase *MQTTButtonComponent::get_entity() const { return this->button_; }

View File

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

View File

@ -14,7 +14,7 @@ static const char *const TAG = "mqtt.climate";
using namespace esphome::climate;
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();
// current_temperature_topic
if (traits.get_supports_current_temperature()) {
@ -29,7 +29,6 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
// mode_state_topic
root[MQTT_MODE_STATE_TOPIC] = this->get_mode_state_topic();
// modes
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
JsonArray modes = root[MQTT_MODES].to<JsonArray>();
// sort array for nice UI in HA
if (traits.supports_mode(CLIMATE_MODE_AUTO))
@ -165,6 +164,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
config.state_topic = false;
config.command_topic = false;
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
void MQTTClimateComponent::setup() {
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());
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return global_mqtt_client->publish_json(
this->get_discovery_topic_(discovery_info),
[this](JsonObject root) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
SendDiscoveryConfig config;
config.state_topic = true;
config.command_topic = true;
@ -189,11 +189,11 @@ bool MQTTComponent::send_discovery_() {
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][1] = mac;
},
this->qos_, discovery_info.retain);
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
uint8_t MQTTComponent::get_qos() const { return this->qos_; }

View File

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

View File

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

View File

@ -45,6 +45,7 @@ void MQTTJSONLightComponent::send_discovery(JsonObject root, mqtt::SendDiscovery
auto traits = this->state_->get_traits();
root[MQTT_COLOR_MODE] = true;
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
JsonArray color_modes = root["supported_color_modes"].to<JsonArray>();
if (traits.supports_color_mode(ColorMode::ON_OFF))
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_; }
void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
const auto &traits = number_->traits;
// 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_MAX] = traits.get_max_value();
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_; }
void MQTTSelectComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
const auto &traits = select_->traits;
// https://www.home-assistant.io/integrations/select.mqtt/
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
JsonArray options = root[MQTT_OPTIONS].to<JsonArray>();
for (const auto &option : traits.get_options())
options.add(option);

View File

@ -20,13 +20,13 @@ MQTTTimeComponent::MQTTTimeComponent(TimeEntity *time) : time_(time) {}
void MQTTTimeComponent::setup() {
this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) {
auto call = this->time_->make_call();
if (root["hour"].is<uint16_t>()) {
if (root["hour"].is<uint8_t>()) {
call.set_hour(root["hour"]);
}
if (root["minute"].is<uint16_t>()) {
if (root["minute"].is<uint8_t>()) {
call.set_minute(root["minute"]);
}
if (root["second"].is<uint16_t>()) {
if (root["second"].is<uint8_t>()) {
call.set_second(root["second"]);
}
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);
}
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) {
set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
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"];
}
});
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
#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);
}
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) {
set_json_id(root, obj, "update-" + obj->get_object_id(), start_config);
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);
}
});
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
#endif