mirror of
https://github.com/esphome/esphome.git
synced 2026-01-26 22:42:06 -07:00
Compare commits
3 Commits
mqtt_enum_
...
nfc_stv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fbf8a518f | ||
|
|
8e68f95a8a | ||
|
|
97e71756e0 |
@@ -1 +1 @@
|
||||
d565b0589e35e692b5f2fc0c14723a99595b4828a3a3ef96c442e86a23176c00
|
||||
15dc295268b2dcf75942f42759b3ddec64eba89f75525698eb39c95a7f4b14ce
|
||||
|
||||
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -58,7 +58,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||
uses: github/codeql-action/init@19b2f06db2b6f5108140aeb04014ef02b648f789 # v4.31.11
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
@@ -86,6 +86,6 @@ jobs:
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
|
||||
uses: github/codeql-action/analyze@19b2f06db2b6f5108140aeb04014ef02b648f789 # v4.31.11
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -482,7 +482,6 @@ esphome/components/switch/* @esphome/core
|
||||
esphome/components/switch/binary_sensor/* @ssieb
|
||||
esphome/components/sx126x/* @swoboda1337
|
||||
esphome/components/sx127x/* @swoboda1337
|
||||
esphome/components/sy6970/* @linkedupbits
|
||||
esphome/components/syslog/* @clydebarrow
|
||||
esphome/components/t6615/* @tylermenezes
|
||||
esphome/components/tc74/* @sethgirvan
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "cover.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include <strings.h>
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::cover {
|
||||
|
||||
static const char *const TAG = "cover";
|
||||
@@ -39,13 +39,13 @@ Cover::Cover() : position{COVER_OPEN} {}
|
||||
|
||||
CoverCall::CoverCall(Cover *parent) : parent_(parent) {}
|
||||
CoverCall &CoverCall::set_command(const char *command) {
|
||||
if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("OPEN")) == 0) {
|
||||
if (strcasecmp(command, "OPEN") == 0) {
|
||||
this->set_command_open();
|
||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("CLOSE")) == 0) {
|
||||
} else if (strcasecmp(command, "CLOSE") == 0) {
|
||||
this->set_command_close();
|
||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("STOP")) == 0) {
|
||||
} else if (strcasecmp(command, "STOP") == 0) {
|
||||
this->set_command_stop();
|
||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("TOGGLE")) == 0) {
|
||||
} else if (strcasecmp(command, "TOGGLE") == 0) {
|
||||
this->set_command_toggle();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command);
|
||||
|
||||
@@ -12,7 +12,6 @@ from esphome.const import (
|
||||
KEY_FRAMEWORK_VERSION,
|
||||
)
|
||||
from esphome.core import CORE
|
||||
from esphome.cpp_generator import add_define
|
||||
|
||||
CODEOWNERS = ["@swoboda1337"]
|
||||
|
||||
@@ -43,7 +42,6 @@ CONFIG_SCHEMA = cv.All(
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
add_define("USE_ESP32_HOSTED")
|
||||
if config[CONF_ACTIVE_HIGH]:
|
||||
esp32.add_idf_sdkconfig_option(
|
||||
"CONFIG_ESP_HOSTED_SDIO_RESET_ACTIVE_HIGH",
|
||||
|
||||
@@ -126,7 +126,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
),
|
||||
cv.Optional(CONF_CA_CERTIFICATE_PATH): cv.All(
|
||||
cv.file_,
|
||||
cv.Any(cv.only_on(PLATFORM_HOST), cv.only_on_esp32),
|
||||
cv.only_on(PLATFORM_HOST),
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
@@ -160,14 +160,7 @@ async def to_code(config):
|
||||
cg.add(var.set_verify_ssl(config[CONF_VERIFY_SSL]))
|
||||
|
||||
if config.get(CONF_VERIFY_SSL):
|
||||
if ca_cert_path := config.get(CONF_CA_CERTIFICATE_PATH):
|
||||
with open(ca_cert_path, encoding="utf-8") as f:
|
||||
ca_cert_content = f.read()
|
||||
cg.add(var.set_ca_certificate(ca_cert_content))
|
||||
else:
|
||||
esp32.add_idf_sdkconfig_option(
|
||||
"CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True
|
||||
)
|
||||
esp32.add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True)
|
||||
|
||||
esp32.add_idf_sdkconfig_option(
|
||||
"CONFIG_ESP_TLS_INSECURE",
|
||||
|
||||
@@ -27,9 +27,8 @@ void HttpRequestIDF::dump_config() {
|
||||
HttpRequestComponent::dump_config();
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Buffer Size RX: %u\n"
|
||||
" Buffer Size TX: %u\n"
|
||||
" Custom CA Certificate: %s",
|
||||
this->buffer_size_rx_, this->buffer_size_tx_, YESNO(this->ca_certificate_ != nullptr));
|
||||
" Buffer Size TX: %u",
|
||||
this->buffer_size_rx_, this->buffer_size_tx_);
|
||||
}
|
||||
|
||||
esp_err_t HttpRequestIDF::http_event_handler(esp_http_client_event_t *evt) {
|
||||
@@ -89,15 +88,11 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::perform(const std::string &url, c
|
||||
config.disable_auto_redirect = !this->follow_redirects_;
|
||||
config.max_redirection_count = this->redirect_limit_;
|
||||
config.auth_type = HTTP_AUTH_TYPE_BASIC;
|
||||
if (secure && this->verify_ssl_) {
|
||||
if (this->ca_certificate_ != nullptr) {
|
||||
config.cert_pem = this->ca_certificate_;
|
||||
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
||||
} else {
|
||||
config.crt_bundle_attach = esp_crt_bundle_attach;
|
||||
#endif
|
||||
}
|
||||
if (secure && this->verify_ssl_) {
|
||||
config.crt_bundle_attach = esp_crt_bundle_attach;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (this->useragent_ != nullptr) {
|
||||
config.user_agent = this->useragent_;
|
||||
|
||||
@@ -35,7 +35,6 @@ class HttpRequestIDF : public HttpRequestComponent {
|
||||
void set_buffer_size_rx(uint16_t buffer_size_rx) { this->buffer_size_rx_ = buffer_size_rx; }
|
||||
void set_buffer_size_tx(uint16_t buffer_size_tx) { this->buffer_size_tx_ = buffer_size_tx; }
|
||||
void set_verify_ssl(bool verify_ssl) { this->verify_ssl_ = verify_ssl; }
|
||||
void set_ca_certificate(const char *ca_certificate) { this->ca_certificate_ = ca_certificate; }
|
||||
|
||||
protected:
|
||||
std::shared_ptr<HttpContainer> perform(const std::string &url, const std::string &method, const std::string &body,
|
||||
@@ -45,7 +44,6 @@ class HttpRequestIDF : public HttpRequestComponent {
|
||||
uint16_t buffer_size_rx_{};
|
||||
uint16_t buffer_size_tx_{};
|
||||
bool verify_ssl_{true};
|
||||
const char *ca_certificate_{nullptr};
|
||||
|
||||
/// @brief Monitors the http client events to gather response headers
|
||||
static esp_err_t http_event_handler(esp_http_client_event_t *evt);
|
||||
|
||||
@@ -82,7 +82,7 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
|
||||
uint32_t last_progress = 0;
|
||||
uint32_t update_start_time = millis();
|
||||
md5::MD5Digest md5_receive;
|
||||
char md5_receive_str[33];
|
||||
std::unique_ptr<char[]> md5_receive_str(new char[33]);
|
||||
|
||||
if (this->md5_expected_.empty() && !this->http_get_md5_()) {
|
||||
return OTA_MD5_INVALID;
|
||||
@@ -176,14 +176,14 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
|
||||
|
||||
// verify MD5 is as expected and act accordingly
|
||||
md5_receive.calculate();
|
||||
md5_receive.get_hex(md5_receive_str);
|
||||
this->md5_computed_ = md5_receive_str;
|
||||
md5_receive.get_hex(md5_receive_str.get());
|
||||
this->md5_computed_ = md5_receive_str.get();
|
||||
if (strncmp(this->md5_computed_.c_str(), this->md5_expected_.c_str(), MD5_SIZE) != 0) {
|
||||
ESP_LOGE(TAG, "MD5 computed: %s - Aborting due to MD5 mismatch", this->md5_computed_.c_str());
|
||||
this->cleanup_(std::move(backend), container);
|
||||
return ota::OTA_RESPONSE_ERROR_MD5_MISMATCH;
|
||||
} else {
|
||||
backend->set_update_md5(md5_receive_str);
|
||||
backend->set_update_md5(md5_receive_str.get());
|
||||
}
|
||||
|
||||
container->end();
|
||||
|
||||
@@ -191,17 +191,10 @@ def _notify_old_style(config):
|
||||
|
||||
|
||||
# The dev and latest branches will be at *least* this version, which is what matters.
|
||||
# Use GitHub releases directly to avoid PlatformIO moderation delays.
|
||||
ARDUINO_VERSIONS = {
|
||||
"dev": (cv.Version(1, 11, 0), "https://github.com/libretiny-eu/libretiny.git"),
|
||||
"latest": (
|
||||
cv.Version(1, 11, 0),
|
||||
"https://github.com/libretiny-eu/libretiny.git#v1.11.0",
|
||||
),
|
||||
"recommended": (
|
||||
cv.Version(1, 11, 0),
|
||||
"https://github.com/libretiny-eu/libretiny.git#v1.11.0",
|
||||
),
|
||||
"dev": (cv.Version(1, 9, 2), "https://github.com/libretiny-eu/libretiny.git"),
|
||||
"latest": (cv.Version(1, 9, 2), "libretiny"),
|
||||
"recommended": (cv.Version(1, 9, 2), None),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,9 @@ namespace esphome::light {
|
||||
|
||||
class AddressableLightWrapper : public light::AddressableLight {
|
||||
public:
|
||||
explicit AddressableLightWrapper(light::LightState *light_state) : light_state_(light_state) {}
|
||||
explicit AddressableLightWrapper(light::LightState *light_state) : light_state_(light_state) {
|
||||
this->wrapper_state_ = new uint8_t[5]; // NOLINT(cppcoreguidelines-owning-memory)
|
||||
}
|
||||
|
||||
int32_t size() const override { return 1; }
|
||||
|
||||
@@ -116,7 +118,7 @@ class AddressableLightWrapper : public light::AddressableLight {
|
||||
}
|
||||
|
||||
light::LightState *light_state_;
|
||||
mutable uint8_t wrapper_state_[5]{};
|
||||
uint8_t *wrapper_state_;
|
||||
ColorMode color_mode_{ColorMode::UNKNOWN};
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome::mhz19 {
|
||||
namespace esphome {
|
||||
namespace mhz19 {
|
||||
|
||||
static const char *const TAG = "mhz19";
|
||||
static const uint8_t MHZ19_REQUEST_LENGTH = 8;
|
||||
@@ -16,19 +17,6 @@ static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_2000PPM[] = {0xFF, 0x01, 0x
|
||||
static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_5000PPM[] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x13, 0x88};
|
||||
static const uint8_t MHZ19_COMMAND_DETECTION_RANGE_0_10000PPM[] = {0xFF, 0x01, 0x99, 0x00, 0x00, 0x00, 0x27, 0x10};
|
||||
|
||||
static const LogString *detection_range_to_log_string(MHZ19DetectionRange range) {
|
||||
switch (range) {
|
||||
case MHZ19_DETECTION_RANGE_0_2000PPM:
|
||||
return LOG_STR("0-2000 ppm");
|
||||
case MHZ19_DETECTION_RANGE_0_5000PPM:
|
||||
return LOG_STR("0-5000 ppm");
|
||||
case MHZ19_DETECTION_RANGE_0_10000PPM:
|
||||
return LOG_STR("0-10000 ppm");
|
||||
default:
|
||||
return LOG_STR("default");
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t mhz19_checksum(const uint8_t *command) {
|
||||
uint8_t sum = 0;
|
||||
for (uint8_t i = 1; i < MHZ19_REQUEST_LENGTH; i++) {
|
||||
@@ -103,24 +91,24 @@ void MHZ19Component::abc_disable() {
|
||||
this->mhz19_write_command_(MHZ19_COMMAND_ABC_DISABLE, nullptr);
|
||||
}
|
||||
|
||||
void MHZ19Component::range_set(MHZ19DetectionRange detection_range) {
|
||||
const uint8_t *command;
|
||||
switch (detection_range) {
|
||||
void MHZ19Component::range_set(MHZ19DetectionRange detection_ppm) {
|
||||
switch (detection_ppm) {
|
||||
case MHZ19_DETECTION_RANGE_DEFAULT:
|
||||
ESP_LOGV(TAG, "Using previously set detection range (no change)");
|
||||
break;
|
||||
case MHZ19_DETECTION_RANGE_0_2000PPM:
|
||||
command = MHZ19_COMMAND_DETECTION_RANGE_0_2000PPM;
|
||||
ESP_LOGD(TAG, "Setting detection range to 0 to 2000ppm");
|
||||
this->mhz19_write_command_(MHZ19_COMMAND_DETECTION_RANGE_0_2000PPM, nullptr);
|
||||
break;
|
||||
case MHZ19_DETECTION_RANGE_0_5000PPM:
|
||||
command = MHZ19_COMMAND_DETECTION_RANGE_0_5000PPM;
|
||||
ESP_LOGD(TAG, "Setting detection range to 0 to 5000ppm");
|
||||
this->mhz19_write_command_(MHZ19_COMMAND_DETECTION_RANGE_0_5000PPM, nullptr);
|
||||
break;
|
||||
case MHZ19_DETECTION_RANGE_0_10000PPM:
|
||||
command = MHZ19_COMMAND_DETECTION_RANGE_0_10000PPM;
|
||||
ESP_LOGD(TAG, "Setting detection range to 0 to 10000ppm");
|
||||
this->mhz19_write_command_(MHZ19_COMMAND_DETECTION_RANGE_0_10000PPM, nullptr);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGV(TAG, "Using previously set detection range (no change)");
|
||||
return;
|
||||
}
|
||||
ESP_LOGD(TAG, "Setting detection range to %s", LOG_STR_ARG(detection_range_to_log_string(detection_range)));
|
||||
this->mhz19_write_command_(command, nullptr);
|
||||
}
|
||||
|
||||
bool MHZ19Component::mhz19_write_command_(const uint8_t *command, uint8_t *response) {
|
||||
@@ -152,7 +140,24 @@ void MHZ19Component::dump_config() {
|
||||
}
|
||||
|
||||
ESP_LOGCONFIG(TAG, " Warmup time: %" PRIu32 " s", this->warmup_seconds_);
|
||||
ESP_LOGCONFIG(TAG, " Detection range: %s", LOG_STR_ARG(detection_range_to_log_string(this->detection_range_)));
|
||||
|
||||
const char *range_str;
|
||||
switch (this->detection_range_) {
|
||||
case MHZ19_DETECTION_RANGE_DEFAULT:
|
||||
range_str = "default";
|
||||
break;
|
||||
case MHZ19_DETECTION_RANGE_0_2000PPM:
|
||||
range_str = "0 to 2000ppm";
|
||||
break;
|
||||
case MHZ19_DETECTION_RANGE_0_5000PPM:
|
||||
range_str = "0 to 5000ppm";
|
||||
break;
|
||||
case MHZ19_DETECTION_RANGE_0_10000PPM:
|
||||
range_str = "0 to 10000ppm";
|
||||
break;
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Detection range: %s", range_str);
|
||||
}
|
||||
|
||||
} // namespace esphome::mhz19
|
||||
} // namespace mhz19
|
||||
} // namespace esphome
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
namespace esphome::mhz19 {
|
||||
namespace esphome {
|
||||
namespace mhz19 {
|
||||
|
||||
enum MHZ19ABCLogic {
|
||||
MHZ19_ABC_NONE = 0,
|
||||
@@ -31,7 +32,7 @@ class MHZ19Component : public PollingComponent, public uart::UARTDevice {
|
||||
void calibrate_zero();
|
||||
void abc_enable();
|
||||
void abc_disable();
|
||||
void range_set(MHZ19DetectionRange detection_range);
|
||||
void range_set(MHZ19DetectionRange detection_ppm);
|
||||
|
||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||
void set_co2_sensor(sensor::Sensor *co2_sensor) { co2_sensor_ = co2_sensor; }
|
||||
@@ -73,4 +74,5 @@ template<typename... Ts> class MHZ19DetectionRangeSetAction : public Action<Ts..
|
||||
void play(const Ts &...x) override { this->parent_->range_set(this->detection_range_.value(x...)); }
|
||||
};
|
||||
|
||||
} // namespace esphome::mhz19
|
||||
} // namespace mhz19
|
||||
} // namespace esphome
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "mqtt_alarm_control_panel.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include "mqtt_const.h"
|
||||
|
||||
@@ -13,33 +12,6 @@ static const char *const TAG = "mqtt.alarm_control_panel";
|
||||
|
||||
using namespace esphome::alarm_control_panel;
|
||||
|
||||
static ProgmemStr alarm_state_to_mqtt_str(AlarmControlPanelState state) {
|
||||
switch (state) {
|
||||
case ACP_STATE_DISARMED:
|
||||
return ESPHOME_F("disarmed");
|
||||
case ACP_STATE_ARMED_HOME:
|
||||
return ESPHOME_F("armed_home");
|
||||
case ACP_STATE_ARMED_AWAY:
|
||||
return ESPHOME_F("armed_away");
|
||||
case ACP_STATE_ARMED_NIGHT:
|
||||
return ESPHOME_F("armed_night");
|
||||
case ACP_STATE_ARMED_VACATION:
|
||||
return ESPHOME_F("armed_vacation");
|
||||
case ACP_STATE_ARMED_CUSTOM_BYPASS:
|
||||
return ESPHOME_F("armed_custom_bypass");
|
||||
case ACP_STATE_PENDING:
|
||||
return ESPHOME_F("pending");
|
||||
case ACP_STATE_ARMING:
|
||||
return ESPHOME_F("arming");
|
||||
case ACP_STATE_DISARMING:
|
||||
return ESPHOME_F("disarming");
|
||||
case ACP_STATE_TRIGGERED:
|
||||
return ESPHOME_F("triggered");
|
||||
default:
|
||||
return ESPHOME_F("unknown");
|
||||
}
|
||||
}
|
||||
|
||||
MQTTAlarmControlPanelComponent::MQTTAlarmControlPanelComponent(AlarmControlPanel *alarm_control_panel)
|
||||
: alarm_control_panel_(alarm_control_panel) {}
|
||||
void MQTTAlarmControlPanelComponent::setup() {
|
||||
@@ -112,9 +84,42 @@ const EntityBase *MQTTAlarmControlPanelComponent::get_entity() const { return th
|
||||
|
||||
bool MQTTAlarmControlPanelComponent::send_initial_state() { return this->publish_state(); }
|
||||
bool MQTTAlarmControlPanelComponent::publish_state() {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish(this->get_state_topic_to_(topic_buf),
|
||||
alarm_state_to_mqtt_str(this->alarm_control_panel_->get_state()));
|
||||
const char *state_s;
|
||||
switch (this->alarm_control_panel_->get_state()) {
|
||||
case ACP_STATE_DISARMED:
|
||||
state_s = "disarmed";
|
||||
break;
|
||||
case ACP_STATE_ARMED_HOME:
|
||||
state_s = "armed_home";
|
||||
break;
|
||||
case ACP_STATE_ARMED_AWAY:
|
||||
state_s = "armed_away";
|
||||
break;
|
||||
case ACP_STATE_ARMED_NIGHT:
|
||||
state_s = "armed_night";
|
||||
break;
|
||||
case ACP_STATE_ARMED_VACATION:
|
||||
state_s = "armed_vacation";
|
||||
break;
|
||||
case ACP_STATE_ARMED_CUSTOM_BYPASS:
|
||||
state_s = "armed_custom_bypass";
|
||||
break;
|
||||
case ACP_STATE_PENDING:
|
||||
state_s = "pending";
|
||||
break;
|
||||
case ACP_STATE_ARMING:
|
||||
state_s = "arming";
|
||||
break;
|
||||
case ACP_STATE_DISARMING:
|
||||
state_s = "disarming";
|
||||
break;
|
||||
case ACP_STATE_TRIGGERED:
|
||||
state_s = "triggered";
|
||||
break;
|
||||
default:
|
||||
state_s = "unknown";
|
||||
}
|
||||
return this->publish(this->get_state_topic_(), state_s);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -52,9 +52,8 @@ bool MQTTBinarySensorComponent::publish_state(bool state) {
|
||||
if (this->binary_sensor_->is_status_binary_sensor())
|
||||
return true;
|
||||
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
const char *state_s = state ? "ON" : "OFF";
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), state_s);
|
||||
return this->publish(this->get_state_topic_(), state_s);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "mqtt_climate.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include "mqtt_const.h"
|
||||
|
||||
@@ -13,111 +12,6 @@ static const char *const TAG = "mqtt.climate";
|
||||
|
||||
using namespace esphome::climate;
|
||||
|
||||
static ProgmemStr climate_mode_to_mqtt_str(ClimateMode mode) {
|
||||
switch (mode) {
|
||||
case CLIMATE_MODE_OFF:
|
||||
return ESPHOME_F("off");
|
||||
case CLIMATE_MODE_HEAT_COOL:
|
||||
return ESPHOME_F("heat_cool");
|
||||
case CLIMATE_MODE_AUTO:
|
||||
return ESPHOME_F("auto");
|
||||
case CLIMATE_MODE_COOL:
|
||||
return ESPHOME_F("cool");
|
||||
case CLIMATE_MODE_HEAT:
|
||||
return ESPHOME_F("heat");
|
||||
case CLIMATE_MODE_FAN_ONLY:
|
||||
return ESPHOME_F("fan_only");
|
||||
case CLIMATE_MODE_DRY:
|
||||
return ESPHOME_F("dry");
|
||||
default:
|
||||
return ESPHOME_F("unknown");
|
||||
}
|
||||
}
|
||||
|
||||
static ProgmemStr climate_action_to_mqtt_str(ClimateAction action) {
|
||||
switch (action) {
|
||||
case CLIMATE_ACTION_OFF:
|
||||
return ESPHOME_F("off");
|
||||
case CLIMATE_ACTION_COOLING:
|
||||
return ESPHOME_F("cooling");
|
||||
case CLIMATE_ACTION_HEATING:
|
||||
return ESPHOME_F("heating");
|
||||
case CLIMATE_ACTION_IDLE:
|
||||
return ESPHOME_F("idle");
|
||||
case CLIMATE_ACTION_DRYING:
|
||||
return ESPHOME_F("drying");
|
||||
case CLIMATE_ACTION_FAN:
|
||||
return ESPHOME_F("fan");
|
||||
default:
|
||||
return ESPHOME_F("unknown");
|
||||
}
|
||||
}
|
||||
|
||||
static ProgmemStr climate_fan_mode_to_mqtt_str(ClimateFanMode fan_mode) {
|
||||
switch (fan_mode) {
|
||||
case CLIMATE_FAN_ON:
|
||||
return ESPHOME_F("on");
|
||||
case CLIMATE_FAN_OFF:
|
||||
return ESPHOME_F("off");
|
||||
case CLIMATE_FAN_AUTO:
|
||||
return ESPHOME_F("auto");
|
||||
case CLIMATE_FAN_LOW:
|
||||
return ESPHOME_F("low");
|
||||
case CLIMATE_FAN_MEDIUM:
|
||||
return ESPHOME_F("medium");
|
||||
case CLIMATE_FAN_HIGH:
|
||||
return ESPHOME_F("high");
|
||||
case CLIMATE_FAN_MIDDLE:
|
||||
return ESPHOME_F("middle");
|
||||
case CLIMATE_FAN_FOCUS:
|
||||
return ESPHOME_F("focus");
|
||||
case CLIMATE_FAN_DIFFUSE:
|
||||
return ESPHOME_F("diffuse");
|
||||
case CLIMATE_FAN_QUIET:
|
||||
return ESPHOME_F("quiet");
|
||||
default:
|
||||
return ESPHOME_F("unknown");
|
||||
}
|
||||
}
|
||||
|
||||
static ProgmemStr climate_swing_mode_to_mqtt_str(ClimateSwingMode swing_mode) {
|
||||
switch (swing_mode) {
|
||||
case CLIMATE_SWING_OFF:
|
||||
return ESPHOME_F("off");
|
||||
case CLIMATE_SWING_BOTH:
|
||||
return ESPHOME_F("both");
|
||||
case CLIMATE_SWING_VERTICAL:
|
||||
return ESPHOME_F("vertical");
|
||||
case CLIMATE_SWING_HORIZONTAL:
|
||||
return ESPHOME_F("horizontal");
|
||||
default:
|
||||
return ESPHOME_F("unknown");
|
||||
}
|
||||
}
|
||||
|
||||
static ProgmemStr climate_preset_to_mqtt_str(ClimatePreset preset) {
|
||||
switch (preset) {
|
||||
case CLIMATE_PRESET_NONE:
|
||||
return ESPHOME_F("none");
|
||||
case CLIMATE_PRESET_HOME:
|
||||
return ESPHOME_F("home");
|
||||
case CLIMATE_PRESET_ECO:
|
||||
return ESPHOME_F("eco");
|
||||
case CLIMATE_PRESET_AWAY:
|
||||
return ESPHOME_F("away");
|
||||
case CLIMATE_PRESET_BOOST:
|
||||
return ESPHOME_F("boost");
|
||||
case CLIMATE_PRESET_COMFORT:
|
||||
return ESPHOME_F("comfort");
|
||||
case CLIMATE_PRESET_SLEEP:
|
||||
return ESPHOME_F("sleep");
|
||||
case CLIMATE_PRESET_ACTIVITY:
|
||||
return ESPHOME_F("activity");
|
||||
default:
|
||||
return ESPHOME_F("unknown");
|
||||
}
|
||||
}
|
||||
|
||||
void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
|
||||
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
auto traits = this->device_->get_traits();
|
||||
@@ -366,8 +260,34 @@ const EntityBase *MQTTClimateComponent::get_entity() const { return this->device
|
||||
bool MQTTClimateComponent::publish_state_() {
|
||||
auto traits = this->device_->get_traits();
|
||||
// mode
|
||||
const char *mode_s;
|
||||
switch (this->device_->mode) {
|
||||
case CLIMATE_MODE_OFF:
|
||||
mode_s = "off";
|
||||
break;
|
||||
case CLIMATE_MODE_AUTO:
|
||||
mode_s = "auto";
|
||||
break;
|
||||
case CLIMATE_MODE_COOL:
|
||||
mode_s = "cool";
|
||||
break;
|
||||
case CLIMATE_MODE_HEAT:
|
||||
mode_s = "heat";
|
||||
break;
|
||||
case CLIMATE_MODE_FAN_ONLY:
|
||||
mode_s = "fan_only";
|
||||
break;
|
||||
case CLIMATE_MODE_DRY:
|
||||
mode_s = "dry";
|
||||
break;
|
||||
case CLIMATE_MODE_HEAT_COOL:
|
||||
mode_s = "heat_cool";
|
||||
break;
|
||||
default:
|
||||
mode_s = "unknown";
|
||||
}
|
||||
bool success = true;
|
||||
if (!this->publish(this->get_mode_state_topic(), climate_mode_to_mqtt_str(this->device_->mode)))
|
||||
if (!this->publish(this->get_mode_state_topic(), mode_s))
|
||||
success = false;
|
||||
int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals();
|
||||
int8_t current_accuracy = traits.get_current_temperature_accuracy_decimals();
|
||||
@@ -407,37 +327,134 @@ bool MQTTClimateComponent::publish_state_() {
|
||||
}
|
||||
|
||||
if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) {
|
||||
if (this->device_->has_custom_preset()) {
|
||||
if (!this->publish(this->get_preset_state_topic(), this->device_->get_custom_preset()))
|
||||
success = false;
|
||||
} else if (this->device_->preset.has_value()) {
|
||||
if (!this->publish(this->get_preset_state_topic(), climate_preset_to_mqtt_str(this->device_->preset.value())))
|
||||
success = false;
|
||||
} else if (!this->publish(this->get_preset_state_topic(), "")) {
|
||||
success = false;
|
||||
std::string payload;
|
||||
if (this->device_->preset.has_value()) {
|
||||
switch (this->device_->preset.value()) {
|
||||
case CLIMATE_PRESET_NONE:
|
||||
payload = "none";
|
||||
break;
|
||||
case CLIMATE_PRESET_HOME:
|
||||
payload = "home";
|
||||
break;
|
||||
case CLIMATE_PRESET_AWAY:
|
||||
payload = "away";
|
||||
break;
|
||||
case CLIMATE_PRESET_BOOST:
|
||||
payload = "boost";
|
||||
break;
|
||||
case CLIMATE_PRESET_COMFORT:
|
||||
payload = "comfort";
|
||||
break;
|
||||
case CLIMATE_PRESET_ECO:
|
||||
payload = "eco";
|
||||
break;
|
||||
case CLIMATE_PRESET_SLEEP:
|
||||
payload = "sleep";
|
||||
break;
|
||||
case CLIMATE_PRESET_ACTIVITY:
|
||||
payload = "activity";
|
||||
break;
|
||||
default:
|
||||
payload = "unknown";
|
||||
}
|
||||
}
|
||||
if (this->device_->has_custom_preset())
|
||||
payload = this->device_->get_custom_preset().c_str();
|
||||
if (!this->publish(this->get_preset_state_topic(), payload))
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION)) {
|
||||
if (!this->publish(this->get_action_state_topic(), climate_action_to_mqtt_str(this->device_->action)))
|
||||
const char *payload;
|
||||
switch (this->device_->action) {
|
||||
case CLIMATE_ACTION_OFF:
|
||||
payload = "off";
|
||||
break;
|
||||
case CLIMATE_ACTION_COOLING:
|
||||
payload = "cooling";
|
||||
break;
|
||||
case CLIMATE_ACTION_HEATING:
|
||||
payload = "heating";
|
||||
break;
|
||||
case CLIMATE_ACTION_IDLE:
|
||||
payload = "idle";
|
||||
break;
|
||||
case CLIMATE_ACTION_DRYING:
|
||||
payload = "drying";
|
||||
break;
|
||||
case CLIMATE_ACTION_FAN:
|
||||
payload = "fan";
|
||||
break;
|
||||
default:
|
||||
payload = "unknown";
|
||||
}
|
||||
if (!this->publish(this->get_action_state_topic(), payload))
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (traits.get_supports_fan_modes()) {
|
||||
if (this->device_->has_custom_fan_mode()) {
|
||||
if (!this->publish(this->get_fan_mode_state_topic(), this->device_->get_custom_fan_mode()))
|
||||
success = false;
|
||||
} else if (this->device_->fan_mode.has_value()) {
|
||||
if (!this->publish(this->get_fan_mode_state_topic(),
|
||||
climate_fan_mode_to_mqtt_str(this->device_->fan_mode.value())))
|
||||
success = false;
|
||||
} else if (!this->publish(this->get_fan_mode_state_topic(), "")) {
|
||||
success = false;
|
||||
std::string payload;
|
||||
if (this->device_->fan_mode.has_value()) {
|
||||
switch (this->device_->fan_mode.value()) {
|
||||
case CLIMATE_FAN_ON:
|
||||
payload = "on";
|
||||
break;
|
||||
case CLIMATE_FAN_OFF:
|
||||
payload = "off";
|
||||
break;
|
||||
case CLIMATE_FAN_AUTO:
|
||||
payload = "auto";
|
||||
break;
|
||||
case CLIMATE_FAN_LOW:
|
||||
payload = "low";
|
||||
break;
|
||||
case CLIMATE_FAN_MEDIUM:
|
||||
payload = "medium";
|
||||
break;
|
||||
case CLIMATE_FAN_HIGH:
|
||||
payload = "high";
|
||||
break;
|
||||
case CLIMATE_FAN_MIDDLE:
|
||||
payload = "middle";
|
||||
break;
|
||||
case CLIMATE_FAN_FOCUS:
|
||||
payload = "focus";
|
||||
break;
|
||||
case CLIMATE_FAN_DIFFUSE:
|
||||
payload = "diffuse";
|
||||
break;
|
||||
case CLIMATE_FAN_QUIET:
|
||||
payload = "quiet";
|
||||
break;
|
||||
default:
|
||||
payload = "unknown";
|
||||
}
|
||||
}
|
||||
if (this->device_->has_custom_fan_mode())
|
||||
payload = this->device_->get_custom_fan_mode().c_str();
|
||||
if (!this->publish(this->get_fan_mode_state_topic(), payload))
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (traits.get_supports_swing_modes()) {
|
||||
if (!this->publish(this->get_swing_mode_state_topic(), climate_swing_mode_to_mqtt_str(this->device_->swing_mode)))
|
||||
const char *payload;
|
||||
switch (this->device_->swing_mode) {
|
||||
case CLIMATE_SWING_OFF:
|
||||
payload = "off";
|
||||
break;
|
||||
case CLIMATE_SWING_BOTH:
|
||||
payload = "both";
|
||||
break;
|
||||
case CLIMATE_SWING_VERTICAL:
|
||||
payload = "vertical";
|
||||
break;
|
||||
case CLIMATE_SWING_HORIZONTAL:
|
||||
payload = "horizontal";
|
||||
break;
|
||||
default:
|
||||
payload = "unknown";
|
||||
}
|
||||
if (!this->publish(this->get_swing_mode_state_topic(), payload))
|
||||
success = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
#include "esphome/core/version.h"
|
||||
|
||||
#include "mqtt_const.h"
|
||||
@@ -133,45 +132,17 @@ std::string MQTTComponent::get_command_topic_() const {
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish(const std::string &topic, const std::string &payload) {
|
||||
return this->publish(topic.c_str(), payload.data(), payload.size());
|
||||
return this->publish(topic, payload.data(), payload.size());
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish(const std::string &topic, const char *payload, size_t payload_length) {
|
||||
return this->publish(topic.c_str(), payload, payload_length);
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish(const char *topic, const char *payload, size_t payload_length) {
|
||||
if (topic[0] == '\0')
|
||||
if (topic.empty())
|
||||
return false;
|
||||
return global_mqtt_client->publish(topic, payload, payload_length, this->qos_, this->retain_);
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish(const char *topic, const char *payload) {
|
||||
return this->publish(topic, payload, strlen(payload));
|
||||
}
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
bool MQTTComponent::publish(const std::string &topic, ProgmemStr payload) {
|
||||
return this->publish(topic.c_str(), payload);
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish(const char *topic, ProgmemStr payload) {
|
||||
if (topic[0] == '\0')
|
||||
return false;
|
||||
// On ESP8266, ProgmemStr is __FlashStringHelper* - need to copy from flash
|
||||
char buf[64];
|
||||
strncpy_P(buf, reinterpret_cast<const char *>(payload), sizeof(buf) - 1);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
return global_mqtt_client->publish(topic, buf, strlen(buf), this->qos_, this->retain_);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool MQTTComponent::publish_json(const std::string &topic, const json::json_build_t &f) {
|
||||
return this->publish_json(topic.c_str(), f);
|
||||
}
|
||||
|
||||
bool MQTTComponent::publish_json(const char *topic, const json::json_build_t &f) {
|
||||
if (topic[0] == '\0')
|
||||
if (topic.empty())
|
||||
return false;
|
||||
return global_mqtt_client->publish_json(topic, f, this->qos_, this->retain_);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/entity_base.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
#include "mqtt_client.h"
|
||||
|
||||
@@ -158,70 +157,6 @@ class MQTTComponent : public Component {
|
||||
*/
|
||||
bool publish(const std::string &topic, const char *payload, size_t payload_length);
|
||||
|
||||
/** Send a MQTT message.
|
||||
*
|
||||
* @param topic The topic.
|
||||
* @param payload The null-terminated payload.
|
||||
*/
|
||||
bool publish(const std::string &topic, const char *payload) {
|
||||
return this->publish(topic.c_str(), payload, strlen(payload));
|
||||
}
|
||||
|
||||
/** Send a MQTT message (no heap allocation for topic).
|
||||
*
|
||||
* @param topic The topic as C string.
|
||||
* @param payload The payload buffer.
|
||||
* @param payload_length The length of the payload.
|
||||
*/
|
||||
bool publish(const char *topic, const char *payload, size_t payload_length);
|
||||
|
||||
/** Send a MQTT message (no heap allocation for topic).
|
||||
*
|
||||
* @param topic The topic as StringRef (for use with get_state_topic_to_()).
|
||||
* @param payload The payload buffer.
|
||||
* @param payload_length The length of the payload.
|
||||
*/
|
||||
bool publish(StringRef topic, const char *payload, size_t payload_length) {
|
||||
return this->publish(topic.c_str(), payload, payload_length);
|
||||
}
|
||||
|
||||
/** Send a MQTT message (no heap allocation for topic).
|
||||
*
|
||||
* @param topic The topic as C string.
|
||||
* @param payload The null-terminated payload.
|
||||
*/
|
||||
bool publish(const char *topic, const char *payload);
|
||||
|
||||
/** Send a MQTT message (no heap allocation for topic).
|
||||
*
|
||||
* @param topic The topic as StringRef (for use with get_state_topic_to_()).
|
||||
* @param payload The null-terminated payload.
|
||||
*/
|
||||
bool publish(StringRef topic, const char *payload) { return this->publish(topic.c_str(), payload); }
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
/** Send a MQTT message with a PROGMEM string payload.
|
||||
*
|
||||
* @param topic The topic.
|
||||
* @param payload The payload (ProgmemStr - stored in flash on ESP8266).
|
||||
*/
|
||||
bool publish(const std::string &topic, ProgmemStr payload);
|
||||
|
||||
/** Send a MQTT message with a PROGMEM string payload (no heap allocation for topic).
|
||||
*
|
||||
* @param topic The topic as C string.
|
||||
* @param payload The payload (ProgmemStr - stored in flash on ESP8266).
|
||||
*/
|
||||
bool publish(const char *topic, ProgmemStr payload);
|
||||
|
||||
/** Send a MQTT message with a PROGMEM string payload (no heap allocation for topic).
|
||||
*
|
||||
* @param topic The topic as StringRef (for use with get_state_topic_to_()).
|
||||
* @param payload The payload (ProgmemStr - stored in flash on ESP8266).
|
||||
*/
|
||||
bool publish(StringRef topic, ProgmemStr payload) { return this->publish(topic.c_str(), payload); }
|
||||
#endif
|
||||
|
||||
/** Construct and send a JSON MQTT message.
|
||||
*
|
||||
* @param topic The topic.
|
||||
@@ -229,20 +164,6 @@ class MQTTComponent : public Component {
|
||||
*/
|
||||
bool publish_json(const std::string &topic, const json::json_build_t &f);
|
||||
|
||||
/** Construct and send a JSON MQTT message (no heap allocation for topic).
|
||||
*
|
||||
* @param topic The topic as C string.
|
||||
* @param f The Json Message builder.
|
||||
*/
|
||||
bool publish_json(const char *topic, const json::json_build_t &f);
|
||||
|
||||
/** Construct and send a JSON MQTT message (no heap allocation for topic).
|
||||
*
|
||||
* @param topic The topic as StringRef (for use with get_state_topic_to_()).
|
||||
* @param f The Json Message builder.
|
||||
*/
|
||||
bool publish_json(StringRef topic, const json::json_build_t &f) { return this->publish_json(topic.c_str(), f); }
|
||||
|
||||
/** Subscribe to a MQTT topic.
|
||||
*
|
||||
* @param topic The topic. Wildcards are currently not supported.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "mqtt_cover.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include "mqtt_const.h"
|
||||
|
||||
@@ -13,20 +12,6 @@ static const char *const TAG = "mqtt.cover";
|
||||
|
||||
using namespace esphome::cover;
|
||||
|
||||
static ProgmemStr cover_state_to_mqtt_str(CoverOperation operation, float position, bool supports_position) {
|
||||
if (operation == COVER_OPERATION_OPENING)
|
||||
return ESPHOME_F("opening");
|
||||
if (operation == COVER_OPERATION_CLOSING)
|
||||
return ESPHOME_F("closing");
|
||||
if (position == COVER_CLOSED)
|
||||
return ESPHOME_F("closed");
|
||||
if (position == COVER_OPEN)
|
||||
return ESPHOME_F("open");
|
||||
if (supports_position)
|
||||
return ESPHOME_F("open");
|
||||
return ESPHOME_F("unknown");
|
||||
}
|
||||
|
||||
MQTTCoverComponent::MQTTCoverComponent(Cover *cover) : cover_(cover) {}
|
||||
void MQTTCoverComponent::setup() {
|
||||
auto traits = this->cover_->get_traits();
|
||||
@@ -124,10 +109,13 @@ bool MQTTCoverComponent::publish_state() {
|
||||
if (!this->publish(this->get_tilt_state_topic(), pos, len))
|
||||
success = false;
|
||||
}
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
if (!this->publish(this->get_state_topic_to_(topic_buf),
|
||||
cover_state_to_mqtt_str(this->cover_->current_operation, this->cover_->position,
|
||||
traits.get_supports_position())))
|
||||
const char *state_s = this->cover_->current_operation == COVER_OPERATION_OPENING ? "opening"
|
||||
: this->cover_->current_operation == COVER_OPERATION_CLOSING ? "closing"
|
||||
: this->cover_->position == COVER_CLOSED ? "closed"
|
||||
: this->cover_->position == COVER_OPEN ? "open"
|
||||
: traits.get_supports_position() ? "open"
|
||||
: "unknown";
|
||||
if (!this->publish(this->get_state_topic_(), state_s))
|
||||
success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -53,8 +53,7 @@ bool MQTTDateComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTDateComponent::publish_state(uint16_t year, uint8_t month, uint8_t day) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [year, month, day](JsonObject root) {
|
||||
return this->publish_json(this->get_state_topic_(), [year, month, day](JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
root[ESPHOME_F("year")] = year;
|
||||
root[ESPHOME_F("month")] = month;
|
||||
|
||||
@@ -66,17 +66,15 @@ bool MQTTDateTimeComponent::send_initial_state() {
|
||||
}
|
||||
bool MQTTDateTimeComponent::publish_state(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute,
|
||||
uint8_t second) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf),
|
||||
[year, month, day, hour, minute, second](JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
root[ESPHOME_F("year")] = year;
|
||||
root[ESPHOME_F("month")] = month;
|
||||
root[ESPHOME_F("day")] = day;
|
||||
root[ESPHOME_F("hour")] = hour;
|
||||
root[ESPHOME_F("minute")] = minute;
|
||||
root[ESPHOME_F("second")] = second;
|
||||
});
|
||||
return this->publish_json(this->get_state_topic_(), [year, month, day, hour, minute, second](JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
root[ESPHOME_F("year")] = year;
|
||||
root[ESPHOME_F("month")] = month;
|
||||
root[ESPHOME_F("day")] = day;
|
||||
root[ESPHOME_F("hour")] = hour;
|
||||
root[ESPHOME_F("minute")] = minute;
|
||||
root[ESPHOME_F("second")] = second;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -44,8 +44,7 @@ void MQTTEventComponent::dump_config() {
|
||||
}
|
||||
|
||||
bool MQTTEventComponent::publish_event_(const std::string &event_type) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [event_type](JsonObject root) {
|
||||
return this->publish_json(this->get_state_topic_(), [event_type](JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
root[MQTT_EVENT_TYPE] = event_type;
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "mqtt_fan.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include "mqtt_const.h"
|
||||
|
||||
@@ -13,14 +12,6 @@ static const char *const TAG = "mqtt.fan";
|
||||
|
||||
using namespace esphome::fan;
|
||||
|
||||
static ProgmemStr fan_direction_to_mqtt_str(FanDirection direction) {
|
||||
return direction == FanDirection::FORWARD ? ESPHOME_F("forward") : ESPHOME_F("reverse");
|
||||
}
|
||||
|
||||
static ProgmemStr fan_oscillation_to_mqtt_str(bool oscillating) {
|
||||
return oscillating ? ESPHOME_F("oscillate_on") : ESPHOME_F("oscillate_off");
|
||||
}
|
||||
|
||||
MQTTFanComponent::MQTTFanComponent(Fan *state) : state_(state) {}
|
||||
|
||||
Fan *MQTTFanComponent::get_state() const { return this->state_; }
|
||||
@@ -167,18 +158,18 @@ void MQTTFanComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig
|
||||
}
|
||||
}
|
||||
bool MQTTFanComponent::publish_state() {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
const char *state_s = this->state_->state ? "ON" : "OFF";
|
||||
ESP_LOGD(TAG, "'%s' Sending state %s.", this->state_->get_name().c_str(), state_s);
|
||||
this->publish(this->get_state_topic_to_(topic_buf), state_s);
|
||||
this->publish(this->get_state_topic_(), state_s);
|
||||
bool failed = false;
|
||||
if (this->state_->get_traits().supports_direction()) {
|
||||
bool success = this->publish(this->get_direction_state_topic(), fan_direction_to_mqtt_str(this->state_->direction));
|
||||
bool success = this->publish(this->get_direction_state_topic(),
|
||||
this->state_->direction == fan::FanDirection::FORWARD ? "forward" : "reverse");
|
||||
failed = failed || !success;
|
||||
}
|
||||
if (this->state_->get_traits().supports_oscillation()) {
|
||||
bool success =
|
||||
this->publish(this->get_oscillation_state_topic(), fan_oscillation_to_mqtt_str(this->state_->oscillating));
|
||||
bool success = this->publish(this->get_oscillation_state_topic(),
|
||||
this->state_->oscillating ? "oscillate_on" : "oscillate_off");
|
||||
failed = failed || !success;
|
||||
}
|
||||
auto traits = this->state_->get_traits();
|
||||
|
||||
@@ -34,8 +34,7 @@ void MQTTJSONLightComponent::on_light_remote_values_update() {
|
||||
MQTTJSONLightComponent::MQTTJSONLightComponent(LightState *state) : state_(state) {}
|
||||
|
||||
bool MQTTJSONLightComponent::publish_state_() {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [this](JsonObject root) {
|
||||
return this->publish_json(this->get_state_topic_(), [this](JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
LightJSONSchema::dump_json(*this->state_, root);
|
||||
});
|
||||
|
||||
@@ -47,14 +47,13 @@ void MQTTLockComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfi
|
||||
bool MQTTLockComponent::send_initial_state() { return this->publish_state(); }
|
||||
|
||||
bool MQTTLockComponent::publish_state() {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
#ifdef USE_STORE_LOG_STR_IN_FLASH
|
||||
char buf[LOCK_STATE_STR_SIZE];
|
||||
strncpy_P(buf, (PGM_P) lock_state_to_string(this->lock_->state), sizeof(buf) - 1);
|
||||
buf[sizeof(buf) - 1] = '\0';
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), buf);
|
||||
return this->publish(this->get_state_topic_(), buf);
|
||||
#else
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), LOG_STR_ARG(lock_state_to_string(this->lock_->state)));
|
||||
return this->publish(this->get_state_topic_(), LOG_STR_ARG(lock_state_to_string(this->lock_->state)));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -74,10 +74,9 @@ bool MQTTNumberComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTNumberComponent::publish_state(float value) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
char buffer[64];
|
||||
size_t len = buf_append_printf(buffer, sizeof(buffer), 0, "%f", value);
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), buffer, len);
|
||||
buf_append_printf(buffer, sizeof(buffer), 0, "%f", value);
|
||||
return this->publish(this->get_state_topic_(), buffer);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -50,8 +50,7 @@ bool MQTTSelectComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTSelectComponent::publish_state(const std::string &value) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), value.data(), value.size());
|
||||
return this->publish(this->get_state_topic_(), value);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -79,13 +79,12 @@ bool MQTTSensorComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTSensorComponent::publish_state(float value) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
if (mqtt::global_mqtt_client->is_publish_nan_as_none() && std::isnan(value))
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), "None", 4);
|
||||
return this->publish(this->get_state_topic_(), "None", 4);
|
||||
int8_t accuracy = this->sensor_->get_accuracy_decimals();
|
||||
char buf[VALUE_ACCURACY_MAX_LEN];
|
||||
size_t len = value_accuracy_to_buf(buf, value, accuracy);
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), buf, len);
|
||||
return this->publish(this->get_state_topic_(), buf, len);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -52,9 +52,8 @@ void MQTTSwitchComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCon
|
||||
bool MQTTSwitchComponent::send_initial_state() { return this->publish_state(this->switch_->state); }
|
||||
|
||||
bool MQTTSwitchComponent::publish_state(bool state) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
const char *state_s = state ? "ON" : "OFF";
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), state_s);
|
||||
return this->publish(this->get_state_topic_(), state_s);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -53,8 +53,7 @@ bool MQTTTextComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTTextComponent::publish_state(const std::string &value) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), value.data(), value.size());
|
||||
return this->publish(this->get_state_topic_(), value);
|
||||
}
|
||||
|
||||
} // namespace esphome::mqtt
|
||||
|
||||
@@ -31,10 +31,7 @@ void MQTTTextSensor::dump_config() {
|
||||
LOG_MQTT_COMPONENT(true, false);
|
||||
}
|
||||
|
||||
bool MQTTTextSensor::publish_state(const std::string &value) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish(this->get_state_topic_to_(topic_buf), value.data(), value.size());
|
||||
}
|
||||
bool MQTTTextSensor::publish_state(const std::string &value) { return this->publish(this->get_state_topic_(), value); }
|
||||
bool MQTTTextSensor::send_initial_state() {
|
||||
if (this->sensor_->has_state()) {
|
||||
return this->publish_state(this->sensor_->state);
|
||||
|
||||
@@ -53,8 +53,7 @@ bool MQTTTimeComponent::send_initial_state() {
|
||||
}
|
||||
}
|
||||
bool MQTTTimeComponent::publish_state(uint8_t hour, uint8_t minute, uint8_t second) {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [hour, minute, second](JsonObject root) {
|
||||
return this->publish_json(this->get_state_topic_(), [hour, minute, second](JsonObject root) {
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
|
||||
root[ESPHOME_F("hour")] = hour;
|
||||
root[ESPHOME_F("minute")] = minute;
|
||||
|
||||
@@ -28,8 +28,7 @@ void MQTTUpdateComponent::setup() {
|
||||
}
|
||||
|
||||
bool MQTTUpdateComponent::publish_state() {
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
return this->publish_json(this->get_state_topic_to_(topic_buf), [this](JsonObject root) {
|
||||
return this->publish_json(this->get_state_topic_(), [this](JsonObject root) {
|
||||
root[ESPHOME_F("installed_version")] = this->update_->update_info.current_version;
|
||||
root[ESPHOME_F("latest_version")] = this->update_->update_info.latest_version;
|
||||
root[ESPHOME_F("title")] = this->update_->update_info.title;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "mqtt_valve.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include "mqtt_const.h"
|
||||
|
||||
@@ -13,20 +12,6 @@ static const char *const TAG = "mqtt.valve";
|
||||
|
||||
using namespace esphome::valve;
|
||||
|
||||
static ProgmemStr valve_state_to_mqtt_str(ValveOperation operation, float position, bool supports_position) {
|
||||
if (operation == VALVE_OPERATION_OPENING)
|
||||
return ESPHOME_F("opening");
|
||||
if (operation == VALVE_OPERATION_CLOSING)
|
||||
return ESPHOME_F("closing");
|
||||
if (position == VALVE_CLOSED)
|
||||
return ESPHOME_F("closed");
|
||||
if (position == VALVE_OPEN)
|
||||
return ESPHOME_F("open");
|
||||
if (supports_position)
|
||||
return ESPHOME_F("open");
|
||||
return ESPHOME_F("unknown");
|
||||
}
|
||||
|
||||
MQTTValveComponent::MQTTValveComponent(Valve *valve) : valve_(valve) {}
|
||||
void MQTTValveComponent::setup() {
|
||||
auto traits = this->valve_->get_traits();
|
||||
@@ -93,10 +78,13 @@ bool MQTTValveComponent::publish_state() {
|
||||
if (!this->publish(this->get_position_state_topic(), pos, len))
|
||||
success = false;
|
||||
}
|
||||
char topic_buf[MQTT_DEFAULT_TOPIC_MAX_LEN];
|
||||
if (!this->publish(this->get_state_topic_to_(topic_buf),
|
||||
valve_state_to_mqtt_str(this->valve_->current_operation, this->valve_->position,
|
||||
traits.get_supports_position())))
|
||||
const char *state_s = this->valve_->current_operation == VALVE_OPERATION_OPENING ? "opening"
|
||||
: this->valve_->current_operation == VALVE_OPERATION_CLOSING ? "closing"
|
||||
: this->valve_->position == VALVE_CLOSED ? "closed"
|
||||
: this->valve_->position == VALVE_OPEN ? "open"
|
||||
: traits.get_supports_position() ? "open"
|
||||
: "unknown";
|
||||
if (!this->publish(this->get_state_topic_(), state_s))
|
||||
success = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ void NfcTagBinarySensor::set_tag_name(const std::string &str) {
|
||||
this->match_tag_name_ = true;
|
||||
}
|
||||
|
||||
void NfcTagBinarySensor::set_uid(const std::vector<uint8_t> &uid) { this->uid_ = uid; }
|
||||
void NfcTagBinarySensor::set_uid(const NfcTagUid &uid) { this->uid_ = uid; }
|
||||
|
||||
bool NfcTagBinarySensor::tag_match_ndef_string(const std::shared_ptr<NdefMessage> &msg) {
|
||||
for (const auto &record : msg->get_records()) {
|
||||
@@ -63,7 +63,7 @@ bool NfcTagBinarySensor::tag_match_tag_name(const std::shared_ptr<NdefMessage> &
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NfcTagBinarySensor::tag_match_uid(const std::vector<uint8_t> &data) {
|
||||
bool NfcTagBinarySensor::tag_match_uid(const NfcTagUid &data) {
|
||||
if (data.size() != this->uid_.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@ class NfcTagBinarySensor : public binary_sensor::BinarySensor,
|
||||
|
||||
void set_ndef_match_string(const std::string &str);
|
||||
void set_tag_name(const std::string &str);
|
||||
void set_uid(const std::vector<uint8_t> &uid);
|
||||
void set_uid(const NfcTagUid &uid);
|
||||
|
||||
bool tag_match_ndef_string(const std::shared_ptr<NdefMessage> &msg);
|
||||
bool tag_match_tag_name(const std::shared_ptr<NdefMessage> &msg);
|
||||
bool tag_match_uid(const std::vector<uint8_t> &data);
|
||||
bool tag_match_uid(const NfcTagUid &data);
|
||||
|
||||
void tag_off(NfcTag &tag) override;
|
||||
void tag_on(NfcTag &tag) override;
|
||||
@@ -31,7 +31,7 @@ class NfcTagBinarySensor : public binary_sensor::BinarySensor,
|
||||
protected:
|
||||
bool match_tag_name_{false};
|
||||
std::string match_string_;
|
||||
std::vector<uint8_t> uid_;
|
||||
NfcTagUid uid_;
|
||||
};
|
||||
|
||||
} // namespace nfc
|
||||
|
||||
@@ -8,19 +8,23 @@ namespace nfc {
|
||||
|
||||
static const char *const TAG = "nfc";
|
||||
|
||||
char *format_uid_to(char *buffer, const std::vector<uint8_t> &uid) {
|
||||
char *format_uid_to(char *buffer, std::span<const uint8_t> uid) {
|
||||
return format_hex_pretty_to(buffer, FORMAT_UID_BUFFER_SIZE, uid.data(), uid.size(), '-');
|
||||
}
|
||||
|
||||
char *format_bytes_to(char *buffer, const std::vector<uint8_t> &bytes) {
|
||||
char *format_bytes_to(char *buffer, std::span<const uint8_t> bytes) {
|
||||
return format_hex_pretty_to(buffer, FORMAT_BYTES_BUFFER_SIZE, bytes.data(), bytes.size(), ' ');
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
// Deprecated wrappers intentionally use heap-allocating version for backward compatibility
|
||||
std::string format_uid(const std::vector<uint8_t> &uid) { return format_hex_pretty(uid, '-', false); } // NOLINT
|
||||
std::string format_bytes(const std::vector<uint8_t> &bytes) { return format_hex_pretty(bytes, ' ', false); } // NOLINT
|
||||
std::string format_uid(std::span<const uint8_t> uid) {
|
||||
return format_hex_pretty(uid.data(), uid.size(), '-', false); // NOLINT
|
||||
}
|
||||
std::string format_bytes(std::span<const uint8_t> bytes) {
|
||||
return format_hex_pretty(bytes.data(), bytes.size(), ' ', false); // NOLINT
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
uint8_t guess_tag_type(uint8_t uid_length) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "ndef_record.h"
|
||||
#include "nfc_tag.h"
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
@@ -56,19 +57,19 @@ static const uint8_t MAD_KEY[6] = {0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5};
|
||||
/// Max UID size is 10 bytes, formatted as "XX-XX-XX-XX-XX-XX-XX-XX-XX-XX\0" = 30 chars
|
||||
static constexpr size_t FORMAT_UID_BUFFER_SIZE = 30;
|
||||
/// Format UID to buffer with '-' separator (e.g., "04-11-22-33"). Returns buffer for inline use.
|
||||
char *format_uid_to(char *buffer, const std::vector<uint8_t> &uid);
|
||||
char *format_uid_to(char *buffer, std::span<const uint8_t> uid);
|
||||
|
||||
/// Buffer size for format_bytes_to (64 bytes max = 192 chars with space separator)
|
||||
static constexpr size_t FORMAT_BYTES_BUFFER_SIZE = 192;
|
||||
/// Format bytes to buffer with ' ' separator (e.g., "04 11 22 33"). Returns buffer for inline use.
|
||||
char *format_bytes_to(char *buffer, const std::vector<uint8_t> &bytes);
|
||||
char *format_bytes_to(char *buffer, std::span<const uint8_t> bytes);
|
||||
|
||||
// Remove before 2026.6.0
|
||||
ESPDEPRECATED("Use format_uid_to() with stack buffer instead. Removed in 2026.6.0", "2025.12.0")
|
||||
std::string format_uid(const std::vector<uint8_t> &uid);
|
||||
std::string format_uid(std::span<const uint8_t> uid);
|
||||
// Remove before 2026.6.0
|
||||
ESPDEPRECATED("Use format_bytes_to() with stack buffer instead. Removed in 2026.6.0", "2025.12.0")
|
||||
std::string format_bytes(const std::vector<uint8_t> &bytes);
|
||||
std::string format_bytes(std::span<const uint8_t> bytes);
|
||||
|
||||
uint8_t guess_tag_type(uint8_t uid_length);
|
||||
uint8_t get_mifare_classic_ndef_start_index(std::vector<uint8_t> &data);
|
||||
|
||||
@@ -10,26 +10,27 @@
|
||||
namespace esphome {
|
||||
namespace nfc {
|
||||
|
||||
// NFC UIDs are 4, 7, or 10 bytes depending on tag type
|
||||
static constexpr size_t NFC_UID_MAX_LENGTH = 10;
|
||||
using NfcTagUid = StaticVector<uint8_t, NFC_UID_MAX_LENGTH>;
|
||||
|
||||
class NfcTag {
|
||||
public:
|
||||
NfcTag() {
|
||||
this->uid_ = {};
|
||||
this->tag_type_ = "Unknown";
|
||||
};
|
||||
NfcTag(std::vector<uint8_t> &uid) {
|
||||
NfcTag() { this->tag_type_ = "Unknown"; };
|
||||
NfcTag(const NfcTagUid &uid) {
|
||||
this->uid_ = uid;
|
||||
this->tag_type_ = "Unknown";
|
||||
};
|
||||
NfcTag(std::vector<uint8_t> &uid, const std::string &tag_type) {
|
||||
NfcTag(const NfcTagUid &uid, const std::string &tag_type) {
|
||||
this->uid_ = uid;
|
||||
this->tag_type_ = tag_type;
|
||||
};
|
||||
NfcTag(std::vector<uint8_t> &uid, const std::string &tag_type, std::unique_ptr<nfc::NdefMessage> ndef_message) {
|
||||
NfcTag(const NfcTagUid &uid, const std::string &tag_type, std::unique_ptr<nfc::NdefMessage> ndef_message) {
|
||||
this->uid_ = uid;
|
||||
this->tag_type_ = tag_type;
|
||||
this->ndef_message_ = std::move(ndef_message);
|
||||
};
|
||||
NfcTag(std::vector<uint8_t> &uid, const std::string &tag_type, std::vector<uint8_t> &ndef_data) {
|
||||
NfcTag(const NfcTagUid &uid, const std::string &tag_type, std::vector<uint8_t> &ndef_data) {
|
||||
this->uid_ = uid;
|
||||
this->tag_type_ = tag_type;
|
||||
this->ndef_message_ = make_unique<NdefMessage>(ndef_data);
|
||||
@@ -41,14 +42,14 @@ class NfcTag {
|
||||
ndef_message_ = make_unique<NdefMessage>(*rhs.ndef_message_);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> &get_uid() { return this->uid_; };
|
||||
NfcTagUid &get_uid() { return this->uid_; };
|
||||
const std::string &get_tag_type() { return this->tag_type_; };
|
||||
bool has_ndef_message() { return this->ndef_message_ != nullptr; };
|
||||
const std::shared_ptr<NdefMessage> &get_ndef_message() { return this->ndef_message_; };
|
||||
void set_ndef_message(std::unique_ptr<NdefMessage> ndef_message) { this->ndef_message_ = std::move(ndef_message); };
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> uid_;
|
||||
NfcTagUid uid_;
|
||||
std::string tag_type_;
|
||||
std::shared_ptr<NdefMessage> ndef_message_;
|
||||
};
|
||||
|
||||
@@ -168,11 +168,11 @@ void PN532::loop() {
|
||||
}
|
||||
|
||||
uint8_t nfcid_length = read[5];
|
||||
std::vector<uint8_t> nfcid(read.begin() + 6, read.begin() + 6 + nfcid_length);
|
||||
if (read.size() < 6U + nfcid_length) {
|
||||
if (nfcid_length > nfc::NFC_UID_MAX_LENGTH || read.size() < 6U + nfcid_length) {
|
||||
// oops, pn532 returned invalid data
|
||||
return;
|
||||
}
|
||||
nfc::NfcTagUid nfcid(read.begin() + 6, read.begin() + 6 + nfcid_length);
|
||||
|
||||
bool report = true;
|
||||
for (auto *bin_sens : this->binary_sensors_) {
|
||||
@@ -358,7 +358,7 @@ void PN532::turn_off_rf_() {
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> PN532::read_tag_(std::vector<uint8_t> &uid) {
|
||||
std::unique_ptr<nfc::NfcTag> PN532::read_tag_(nfc::NfcTagUid &uid) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
|
||||
if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
|
||||
@@ -393,7 +393,7 @@ void PN532::write_mode(nfc::NdefMessage *message) {
|
||||
ESP_LOGD(TAG, "Waiting to write next tag");
|
||||
}
|
||||
|
||||
bool PN532::clean_tag_(std::vector<uint8_t> &uid) {
|
||||
bool PN532::clean_tag_(nfc::NfcTagUid &uid) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
|
||||
return this->format_mifare_classic_mifare_(uid);
|
||||
@@ -404,7 +404,7 @@ bool PN532::clean_tag_(std::vector<uint8_t> &uid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PN532::format_tag_(std::vector<uint8_t> &uid) {
|
||||
bool PN532::format_tag_(nfc::NfcTagUid &uid) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
|
||||
return this->format_mifare_classic_ndef_(uid);
|
||||
@@ -415,7 +415,7 @@ bool PN532::format_tag_(std::vector<uint8_t> &uid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PN532::write_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message) {
|
||||
bool PN532::write_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
if (type == nfc::TAG_TYPE_MIFARE_CLASSIC) {
|
||||
return this->write_mifare_classic_tag_(uid, message);
|
||||
@@ -448,7 +448,7 @@ void PN532::dump_config() {
|
||||
}
|
||||
}
|
||||
|
||||
bool PN532BinarySensor::process(std::vector<uint8_t> &data) {
|
||||
bool PN532BinarySensor::process(const nfc::NfcTagUid &data) {
|
||||
if (data.size() != this->uid_.size())
|
||||
return false;
|
||||
|
||||
|
||||
@@ -69,28 +69,28 @@ class PN532 : public PollingComponent {
|
||||
virtual bool read_data(std::vector<uint8_t> &data, uint8_t len) = 0;
|
||||
virtual bool read_response(uint8_t command, std::vector<uint8_t> &data) = 0;
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> read_tag_(std::vector<uint8_t> &uid);
|
||||
std::unique_ptr<nfc::NfcTag> read_tag_(nfc::NfcTagUid &uid);
|
||||
|
||||
bool format_tag_(std::vector<uint8_t> &uid);
|
||||
bool clean_tag_(std::vector<uint8_t> &uid);
|
||||
bool write_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message);
|
||||
bool format_tag_(nfc::NfcTagUid &uid);
|
||||
bool clean_tag_(nfc::NfcTagUid &uid);
|
||||
bool write_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message);
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> read_mifare_classic_tag_(std::vector<uint8_t> &uid);
|
||||
std::unique_ptr<nfc::NfcTag> read_mifare_classic_tag_(nfc::NfcTagUid &uid);
|
||||
bool read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data);
|
||||
bool write_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &data);
|
||||
bool auth_mifare_classic_block_(std::vector<uint8_t> &uid, uint8_t block_num, uint8_t key_num, const uint8_t *key);
|
||||
bool format_mifare_classic_mifare_(std::vector<uint8_t> &uid);
|
||||
bool format_mifare_classic_ndef_(std::vector<uint8_t> &uid);
|
||||
bool write_mifare_classic_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message);
|
||||
bool auth_mifare_classic_block_(nfc::NfcTagUid &uid, uint8_t block_num, uint8_t key_num, const uint8_t *key);
|
||||
bool format_mifare_classic_mifare_(nfc::NfcTagUid &uid);
|
||||
bool format_mifare_classic_ndef_(nfc::NfcTagUid &uid);
|
||||
bool write_mifare_classic_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message);
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> read_mifare_ultralight_tag_(std::vector<uint8_t> &uid);
|
||||
std::unique_ptr<nfc::NfcTag> read_mifare_ultralight_tag_(nfc::NfcTagUid &uid);
|
||||
bool read_mifare_ultralight_bytes_(uint8_t start_page, uint16_t num_bytes, std::vector<uint8_t> &data);
|
||||
bool is_mifare_ultralight_formatted_(const std::vector<uint8_t> &page_3_to_6);
|
||||
uint16_t read_mifare_ultralight_capacity_();
|
||||
bool find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_to_6, uint8_t &message_length,
|
||||
uint8_t &message_start_index);
|
||||
bool write_mifare_ultralight_page_(uint8_t page_num, std::vector<uint8_t> &write_data);
|
||||
bool write_mifare_ultralight_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message);
|
||||
bool write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message);
|
||||
bool clean_mifare_ultralight_();
|
||||
|
||||
bool updates_enabled_{true};
|
||||
@@ -98,7 +98,7 @@ class PN532 : public PollingComponent {
|
||||
std::vector<PN532BinarySensor *> binary_sensors_;
|
||||
std::vector<nfc::NfcOnTagTrigger *> triggers_ontag_;
|
||||
std::vector<nfc::NfcOnTagTrigger *> triggers_ontagremoved_;
|
||||
std::vector<uint8_t> current_uid_;
|
||||
nfc::NfcTagUid current_uid_;
|
||||
nfc::NdefMessage *next_task_message_to_write_;
|
||||
uint32_t rd_start_time_{0};
|
||||
enum PN532ReadReady rd_ready_ { WOULDBLOCK };
|
||||
@@ -118,9 +118,9 @@ class PN532 : public PollingComponent {
|
||||
|
||||
class PN532BinarySensor : public binary_sensor::BinarySensor {
|
||||
public:
|
||||
void set_uid(const std::vector<uint8_t> &uid) { uid_ = uid; }
|
||||
void set_uid(const nfc::NfcTagUid &uid) { uid_ = uid; }
|
||||
|
||||
bool process(std::vector<uint8_t> &data);
|
||||
bool process(const nfc::NfcTagUid &data);
|
||||
|
||||
void on_scan_end() {
|
||||
if (!this->found_) {
|
||||
@@ -130,7 +130,7 @@ class PN532BinarySensor : public binary_sensor::BinarySensor {
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> uid_;
|
||||
nfc::NfcTagUid uid_;
|
||||
bool found_{false};
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace pn532 {
|
||||
|
||||
static const char *const TAG = "pn532.mifare_classic";
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> PN532::read_mifare_classic_tag_(std::vector<uint8_t> &uid) {
|
||||
std::unique_ptr<nfc::NfcTag> PN532::read_mifare_classic_tag_(nfc::NfcTagUid &uid) {
|
||||
uint8_t current_block = 4;
|
||||
uint8_t message_start_index = 0;
|
||||
uint32_t message_length = 0;
|
||||
@@ -82,8 +82,7 @@ bool PN532::read_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t> &
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PN532::auth_mifare_classic_block_(std::vector<uint8_t> &uid, uint8_t block_num, uint8_t key_num,
|
||||
const uint8_t *key) {
|
||||
bool PN532::auth_mifare_classic_block_(nfc::NfcTagUid &uid, uint8_t block_num, uint8_t key_num, const uint8_t *key) {
|
||||
std::vector<uint8_t> data({
|
||||
PN532_COMMAND_INDATAEXCHANGE,
|
||||
0x01, // One card
|
||||
@@ -106,7 +105,7 @@ bool PN532::auth_mifare_classic_block_(std::vector<uint8_t> &uid, uint8_t block_
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PN532::format_mifare_classic_mifare_(std::vector<uint8_t> &uid) {
|
||||
bool PN532::format_mifare_classic_mifare_(nfc::NfcTagUid &uid) {
|
||||
std::vector<uint8_t> blank_buffer(
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
std::vector<uint8_t> trailer_buffer(
|
||||
@@ -141,7 +140,7 @@ bool PN532::format_mifare_classic_mifare_(std::vector<uint8_t> &uid) {
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool PN532::format_mifare_classic_ndef_(std::vector<uint8_t> &uid) {
|
||||
bool PN532::format_mifare_classic_ndef_(nfc::NfcTagUid &uid) {
|
||||
std::vector<uint8_t> empty_ndef_message(
|
||||
{0x03, 0x03, 0xD0, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
|
||||
std::vector<uint8_t> blank_block(
|
||||
@@ -216,7 +215,7 @@ bool PN532::write_mifare_classic_block_(uint8_t block_num, std::vector<uint8_t>
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PN532::write_mifare_classic_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message) {
|
||||
bool PN532::write_mifare_classic_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message) {
|
||||
auto encoded = message->encode();
|
||||
|
||||
uint32_t message_length = encoded.size();
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace pn532 {
|
||||
|
||||
static const char *const TAG = "pn532.mifare_ultralight";
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> PN532::read_mifare_ultralight_tag_(std::vector<uint8_t> &uid) {
|
||||
std::unique_ptr<nfc::NfcTag> PN532::read_mifare_ultralight_tag_(nfc::NfcTagUid &uid) {
|
||||
std::vector<uint8_t> data;
|
||||
// pages 3 to 6 contain various info we are interested in -- do one read to grab it all
|
||||
if (!this->read_mifare_ultralight_bytes_(3, nfc::MIFARE_ULTRALIGHT_PAGE_SIZE * nfc::MIFARE_ULTRALIGHT_READ_SIZE,
|
||||
@@ -114,7 +114,7 @@ bool PN532::find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_to_6
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PN532::write_mifare_ultralight_tag_(std::vector<uint8_t> &uid, nfc::NdefMessage *message) {
|
||||
bool PN532::write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, nfc::NdefMessage *message) {
|
||||
uint32_t capacity = this->read_mifare_ultralight_capacity_();
|
||||
|
||||
auto encoded = message->encode();
|
||||
|
||||
@@ -478,7 +478,7 @@ uint8_t PN7150::read_endpoint_data_(nfc::NfcTag &tag) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7150::clean_endpoint_(std::vector<uint8_t> &uid) {
|
||||
uint8_t PN7150::clean_endpoint_(nfc::NfcTagUid &uid) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
switch (type) {
|
||||
case nfc::TAG_TYPE_MIFARE_CLASSIC:
|
||||
@@ -494,7 +494,7 @@ uint8_t PN7150::clean_endpoint_(std::vector<uint8_t> &uid) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7150::format_endpoint_(std::vector<uint8_t> &uid) {
|
||||
uint8_t PN7150::format_endpoint_(nfc::NfcTagUid &uid) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
switch (type) {
|
||||
case nfc::TAG_TYPE_MIFARE_CLASSIC:
|
||||
@@ -510,7 +510,7 @@ uint8_t PN7150::format_endpoint_(std::vector<uint8_t> &uid) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7150::write_endpoint_(std::vector<uint8_t> &uid, std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint8_t PN7150::write_endpoint_(nfc::NfcTagUid &uid, std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
switch (type) {
|
||||
case nfc::TAG_TYPE_MIFARE_CLASSIC:
|
||||
@@ -534,7 +534,7 @@ std::unique_ptr<nfc::NfcTag> PN7150::build_tag_(const uint8_t mode_tech, const s
|
||||
ESP_LOGE(TAG, "UID length cannot be zero");
|
||||
return nullptr;
|
||||
}
|
||||
std::vector<uint8_t> uid(data.begin() + 3, data.begin() + 3 + uid_length);
|
||||
nfc::NfcTagUid uid(data.begin() + 3, data.begin() + 3 + uid_length);
|
||||
const auto *tag_type_str =
|
||||
nfc::guess_tag_type(uid_length) == nfc::TAG_TYPE_MIFARE_CLASSIC ? nfc::MIFARE_CLASSIC : nfc::NFC_FORUM_TYPE_2;
|
||||
return make_unique<nfc::NfcTag>(uid, tag_type_str);
|
||||
@@ -543,7 +543,7 @@ std::unique_ptr<nfc::NfcTag> PN7150::build_tag_(const uint8_t mode_tech, const s
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
optional<size_t> PN7150::find_tag_uid_(const std::vector<uint8_t> &uid) {
|
||||
optional<size_t> PN7150::find_tag_uid_(const nfc::NfcTagUid &uid) {
|
||||
if (!this->discovered_endpoint_.empty()) {
|
||||
for (size_t i = 0; i < this->discovered_endpoint_.size(); i++) {
|
||||
auto existing_tag_uid = this->discovered_endpoint_[i].tag->get_uid();
|
||||
|
||||
@@ -203,12 +203,12 @@ class PN7150 : public nfc::Nfcc, public Component {
|
||||
void select_endpoint_();
|
||||
|
||||
uint8_t read_endpoint_data_(nfc::NfcTag &tag);
|
||||
uint8_t clean_endpoint_(std::vector<uint8_t> &uid);
|
||||
uint8_t format_endpoint_(std::vector<uint8_t> &uid);
|
||||
uint8_t write_endpoint_(std::vector<uint8_t> &uid, std::shared_ptr<nfc::NdefMessage> &message);
|
||||
uint8_t clean_endpoint_(nfc::NfcTagUid &uid);
|
||||
uint8_t format_endpoint_(nfc::NfcTagUid &uid);
|
||||
uint8_t write_endpoint_(nfc::NfcTagUid &uid, std::shared_ptr<nfc::NdefMessage> &message);
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> build_tag_(uint8_t mode_tech, const std::vector<uint8_t> &data);
|
||||
optional<size_t> find_tag_uid_(const std::vector<uint8_t> &uid);
|
||||
optional<size_t> find_tag_uid_(const nfc::NfcTagUid &uid);
|
||||
void purge_old_tags_();
|
||||
void erase_tag_(uint8_t tag_index);
|
||||
|
||||
@@ -251,7 +251,7 @@ class PN7150 : public nfc::Nfcc, public Component {
|
||||
uint8_t find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_to_6, uint8_t &message_length,
|
||||
uint8_t &message_start_index);
|
||||
uint8_t write_mifare_ultralight_page_(uint8_t page_num, std::vector<uint8_t> &write_data);
|
||||
uint8_t write_mifare_ultralight_tag_(std::vector<uint8_t> &uid, const std::shared_ptr<nfc::NdefMessage> &message);
|
||||
uint8_t write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, const std::shared_ptr<nfc::NdefMessage> &message);
|
||||
uint8_t clean_mifare_ultralight_();
|
||||
|
||||
enum NfcTask : uint8_t {
|
||||
|
||||
@@ -115,8 +115,7 @@ uint8_t PN7150::find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7150::write_mifare_ultralight_tag_(std::vector<uint8_t> &uid,
|
||||
const std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint8_t PN7150::write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, const std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint32_t capacity = this->read_mifare_ultralight_capacity_();
|
||||
|
||||
auto encoded = message->encode();
|
||||
|
||||
@@ -506,7 +506,7 @@ uint8_t PN7160::read_endpoint_data_(nfc::NfcTag &tag) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7160::clean_endpoint_(std::vector<uint8_t> &uid) {
|
||||
uint8_t PN7160::clean_endpoint_(nfc::NfcTagUid &uid) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
switch (type) {
|
||||
case nfc::TAG_TYPE_MIFARE_CLASSIC:
|
||||
@@ -522,7 +522,7 @@ uint8_t PN7160::clean_endpoint_(std::vector<uint8_t> &uid) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7160::format_endpoint_(std::vector<uint8_t> &uid) {
|
||||
uint8_t PN7160::format_endpoint_(nfc::NfcTagUid &uid) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
switch (type) {
|
||||
case nfc::TAG_TYPE_MIFARE_CLASSIC:
|
||||
@@ -538,7 +538,7 @@ uint8_t PN7160::format_endpoint_(std::vector<uint8_t> &uid) {
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7160::write_endpoint_(std::vector<uint8_t> &uid, std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint8_t PN7160::write_endpoint_(nfc::NfcTagUid &uid, std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint8_t type = nfc::guess_tag_type(uid.size());
|
||||
switch (type) {
|
||||
case nfc::TAG_TYPE_MIFARE_CLASSIC:
|
||||
@@ -562,7 +562,7 @@ std::unique_ptr<nfc::NfcTag> PN7160::build_tag_(const uint8_t mode_tech, const s
|
||||
ESP_LOGE(TAG, "UID length cannot be zero");
|
||||
return nullptr;
|
||||
}
|
||||
std::vector<uint8_t> uid(data.begin() + 3, data.begin() + 3 + uid_length);
|
||||
nfc::NfcTagUid uid(data.begin() + 3, data.begin() + 3 + uid_length);
|
||||
const auto *tag_type_str =
|
||||
nfc::guess_tag_type(uid_length) == nfc::TAG_TYPE_MIFARE_CLASSIC ? nfc::MIFARE_CLASSIC : nfc::NFC_FORUM_TYPE_2;
|
||||
return make_unique<nfc::NfcTag>(uid, tag_type_str);
|
||||
@@ -571,7 +571,7 @@ std::unique_ptr<nfc::NfcTag> PN7160::build_tag_(const uint8_t mode_tech, const s
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
optional<size_t> PN7160::find_tag_uid_(const std::vector<uint8_t> &uid) {
|
||||
optional<size_t> PN7160::find_tag_uid_(const nfc::NfcTagUid &uid) {
|
||||
if (!this->discovered_endpoint_.empty()) {
|
||||
for (size_t i = 0; i < this->discovered_endpoint_.size(); i++) {
|
||||
auto existing_tag_uid = this->discovered_endpoint_[i].tag->get_uid();
|
||||
|
||||
@@ -220,12 +220,12 @@ class PN7160 : public nfc::Nfcc, public Component {
|
||||
void select_endpoint_();
|
||||
|
||||
uint8_t read_endpoint_data_(nfc::NfcTag &tag);
|
||||
uint8_t clean_endpoint_(std::vector<uint8_t> &uid);
|
||||
uint8_t format_endpoint_(std::vector<uint8_t> &uid);
|
||||
uint8_t write_endpoint_(std::vector<uint8_t> &uid, std::shared_ptr<nfc::NdefMessage> &message);
|
||||
uint8_t clean_endpoint_(nfc::NfcTagUid &uid);
|
||||
uint8_t format_endpoint_(nfc::NfcTagUid &uid);
|
||||
uint8_t write_endpoint_(nfc::NfcTagUid &uid, std::shared_ptr<nfc::NdefMessage> &message);
|
||||
|
||||
std::unique_ptr<nfc::NfcTag> build_tag_(uint8_t mode_tech, const std::vector<uint8_t> &data);
|
||||
optional<size_t> find_tag_uid_(const std::vector<uint8_t> &uid);
|
||||
optional<size_t> find_tag_uid_(const nfc::NfcTagUid &uid);
|
||||
void purge_old_tags_();
|
||||
void erase_tag_(uint8_t tag_index);
|
||||
|
||||
@@ -268,7 +268,7 @@ class PN7160 : public nfc::Nfcc, public Component {
|
||||
uint8_t find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_to_6, uint8_t &message_length,
|
||||
uint8_t &message_start_index);
|
||||
uint8_t write_mifare_ultralight_page_(uint8_t page_num, std::vector<uint8_t> &write_data);
|
||||
uint8_t write_mifare_ultralight_tag_(std::vector<uint8_t> &uid, const std::shared_ptr<nfc::NdefMessage> &message);
|
||||
uint8_t write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, const std::shared_ptr<nfc::NdefMessage> &message);
|
||||
uint8_t clean_mifare_ultralight_();
|
||||
|
||||
enum NfcTask : uint8_t {
|
||||
|
||||
@@ -115,8 +115,7 @@ uint8_t PN7160::find_mifare_ultralight_ndef_(const std::vector<uint8_t> &page_3_
|
||||
return nfc::STATUS_FAILED;
|
||||
}
|
||||
|
||||
uint8_t PN7160::write_mifare_ultralight_tag_(std::vector<uint8_t> &uid,
|
||||
const std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint8_t PN7160::write_mifare_ultralight_tag_(nfc::NfcTagUid &uid, const std::shared_ptr<nfc::NdefMessage> &message) {
|
||||
uint32_t capacity = this->read_mifare_ultralight_capacity_();
|
||||
|
||||
auto encoded = message->encode();
|
||||
|
||||
@@ -30,19 +30,6 @@ static const int8_t SEN5X_INDEX_SCALE_FACTOR = 10; //
|
||||
static const int8_t SEN5X_MIN_INDEX_VALUE = 1 * SEN5X_INDEX_SCALE_FACTOR; // must be adjusted by the scale factor
|
||||
static const int16_t SEN5X_MAX_INDEX_VALUE = 500 * SEN5X_INDEX_SCALE_FACTOR; // must be adjusted by the scale factor
|
||||
|
||||
static const LogString *type_to_string(Sen5xType type) {
|
||||
switch (type) {
|
||||
case Sen5xType::SEN50:
|
||||
return LOG_STR("SEN50");
|
||||
case Sen5xType::SEN54:
|
||||
return LOG_STR("SEN54");
|
||||
case Sen5xType::SEN55:
|
||||
return LOG_STR("SEN55");
|
||||
default:
|
||||
return LOG_STR("UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
static const LogString *rht_accel_mode_to_string(RhtAccelerationMode mode) {
|
||||
switch (mode) {
|
||||
case LOW_ACCELERATION:
|
||||
@@ -56,15 +43,6 @@ static const LogString *rht_accel_mode_to_string(RhtAccelerationMode mode) {
|
||||
}
|
||||
}
|
||||
|
||||
// This function performs an in-place conversion of the provided buffer
|
||||
// from uint16_t values to big endianness
|
||||
static inline const char *sensirion_convert_to_string_in_place(uint16_t *array, size_t length) {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
array[i] = convert_big_endian(array[i]);
|
||||
}
|
||||
return reinterpret_cast<const char *>(array);
|
||||
}
|
||||
|
||||
void SEN5XComponent::setup() {
|
||||
// the sensor needs 1000 ms to enter the idle state
|
||||
this->set_timeout(1000, [this]() {
|
||||
@@ -97,18 +75,18 @@ void SEN5XComponent::setup() {
|
||||
stop_measurement_delay = 200;
|
||||
}
|
||||
this->set_timeout(stop_measurement_delay, [this]() {
|
||||
// note: serial number register is actually 32-bytes long but we grab only the first 16-bytes,
|
||||
// this appears to be all that Sensirion uses for serial numbers, this could change
|
||||
uint16_t raw_serial_number[8];
|
||||
if (!this->get_register(SEN5X_CMD_GET_SERIAL_NUMBER, raw_serial_number, 8, 20)) {
|
||||
uint16_t raw_serial_number[3];
|
||||
if (!this->get_register(SEN5X_CMD_GET_SERIAL_NUMBER, raw_serial_number, 3, 20)) {
|
||||
ESP_LOGE(TAG, "Failed to read serial number");
|
||||
this->error_code_ = SERIAL_NUMBER_IDENTIFICATION_FAILED;
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
const char *serial_number = sensirion_convert_to_string_in_place(raw_serial_number, 8);
|
||||
snprintf(this->serial_number_, sizeof(this->serial_number_), "%s", serial_number);
|
||||
ESP_LOGV(TAG, "Serial number %s", this->serial_number_);
|
||||
this->serial_number_[0] = static_cast<bool>(uint16_t(raw_serial_number[0]) & 0xFF);
|
||||
this->serial_number_[1] = static_cast<uint16_t>(raw_serial_number[0] & 0xFF);
|
||||
this->serial_number_[2] = static_cast<uint16_t>(raw_serial_number[1] >> 8);
|
||||
ESP_LOGV(TAG, "Serial number %02d.%02d.%02d", this->serial_number_[0], this->serial_number_[1],
|
||||
this->serial_number_[2]);
|
||||
|
||||
uint16_t raw_product_name[16];
|
||||
if (!this->get_register(SEN5X_CMD_GET_PRODUCT_NAME, raw_product_name, 16, 20)) {
|
||||
@@ -117,35 +95,50 @@ void SEN5XComponent::setup() {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
const char *product_name = sensirion_convert_to_string_in_place(raw_product_name, 16);
|
||||
if (strncmp(product_name, "SEN50", 5) == 0) {
|
||||
this->type_ = Sen5xType::SEN50;
|
||||
} else if (strncmp(product_name, "SEN54", 5) == 0) {
|
||||
this->type_ = Sen5xType::SEN54;
|
||||
} else if (strncmp(product_name, "SEN55", 5) == 0) {
|
||||
this->type_ = Sen5xType::SEN55;
|
||||
} else {
|
||||
this->type_ = Sen5xType::UNKNOWN;
|
||||
ESP_LOGE(TAG, "Unknown product name: %.32s", product_name);
|
||||
this->error_code_ = PRODUCT_NAME_FAILED;
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
// 2 ASCII bytes are encoded in an int
|
||||
const uint16_t *current_int = raw_product_name;
|
||||
char current_char;
|
||||
uint8_t max = 16;
|
||||
do {
|
||||
// first char
|
||||
current_char = *current_int >> 8;
|
||||
if (current_char) {
|
||||
this->product_name_.push_back(current_char);
|
||||
// second char
|
||||
current_char = *current_int & 0xFF;
|
||||
if (current_char) {
|
||||
this->product_name_.push_back(current_char);
|
||||
}
|
||||
}
|
||||
current_int++;
|
||||
} while (current_char && --max);
|
||||
|
||||
ESP_LOGD(TAG, "Type: %s", LOG_STR_ARG(type_to_string(this->type_)));
|
||||
if (this->humidity_sensor_ && this->type_ == Sen5xType::SEN50) {
|
||||
Sen5xType sen5x_type = UNKNOWN;
|
||||
if (this->product_name_ == "SEN50") {
|
||||
sen5x_type = SEN50;
|
||||
} else {
|
||||
if (this->product_name_ == "SEN54") {
|
||||
sen5x_type = SEN54;
|
||||
} else {
|
||||
if (this->product_name_ == "SEN55") {
|
||||
sen5x_type = SEN55;
|
||||
}
|
||||
}
|
||||
}
|
||||
ESP_LOGD(TAG, "Product name: %s", this->product_name_.c_str());
|
||||
if (this->humidity_sensor_ && sen5x_type == SEN50) {
|
||||
ESP_LOGE(TAG, "Relative humidity requires a SEN54 or SEN55");
|
||||
this->humidity_sensor_ = nullptr; // mark as not used
|
||||
}
|
||||
if (this->temperature_sensor_ && this->type_ == Sen5xType::SEN50) {
|
||||
if (this->temperature_sensor_ && sen5x_type == SEN50) {
|
||||
ESP_LOGE(TAG, "Temperature requires a SEN54 or SEN55");
|
||||
this->temperature_sensor_ = nullptr; // mark as not used
|
||||
}
|
||||
if (this->voc_sensor_ && this->type_ == Sen5xType::SEN50) {
|
||||
if (this->voc_sensor_ && sen5x_type == SEN50) {
|
||||
ESP_LOGE(TAG, "VOC requires a SEN54 or SEN55");
|
||||
this->voc_sensor_ = nullptr; // mark as not used
|
||||
}
|
||||
if (this->nox_sensor_ && this->type_ != Sen5xType::SEN55) {
|
||||
if (this->nox_sensor_ && sen5x_type != SEN55) {
|
||||
ESP_LOGE(TAG, "NOx requires a SEN55");
|
||||
this->nox_sensor_ = nullptr; // mark as not used
|
||||
}
|
||||
@@ -160,8 +153,12 @@ void SEN5XComponent::setup() {
|
||||
ESP_LOGV(TAG, "Firmware version %d", this->firmware_version_);
|
||||
|
||||
if (this->voc_sensor_ && this->store_baseline_) {
|
||||
// Hash with serial number, serial numbers are unique, so multiple sensors can be used without conflict
|
||||
uint32_t hash = fnv1a_hash(this->serial_number_);
|
||||
uint32_t combined_serial =
|
||||
encode_uint24(this->serial_number_[0], this->serial_number_[1], this->serial_number_[2]);
|
||||
// Hash with config hash, version, and serial number
|
||||
// This ensures the baseline storage is cleared after OTA
|
||||
// Serial numbers are unique to each sensor, so multiple sensors can be used without conflict
|
||||
uint32_t hash = fnv1a_hash_extend(App.get_config_version_hash(), combined_serial);
|
||||
this->pref_ = global_preferences->make_preference<uint16_t[4]>(hash, true);
|
||||
this->voc_baseline_time_ = App.get_loop_component_start_time();
|
||||
if (this->pref_.load(&this->voc_baseline_state_)) {
|
||||
@@ -265,10 +262,11 @@ void SEN5XComponent::dump_config() {
|
||||
}
|
||||
}
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Type: %s\n"
|
||||
" Product name: %s\n"
|
||||
" Firmware version: %d\n"
|
||||
" Serial number: %s",
|
||||
LOG_STR_ARG(type_to_string(this->type_)), this->firmware_version_, this->serial_number_);
|
||||
" Serial number %02d.%02d.%02d",
|
||||
this->product_name_.c_str(), this->firmware_version_, this->serial_number_[0], this->serial_number_[1],
|
||||
this->serial_number_[2]);
|
||||
if (this->auto_cleaning_interval_.has_value()) {
|
||||
ESP_LOGCONFIG(TAG, " Auto cleaning interval: %" PRId32 "s", this->auto_cleaning_interval_.value());
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ enum RhtAccelerationMode : uint16_t {
|
||||
HIGH_ACCELERATION = 2,
|
||||
};
|
||||
|
||||
enum class Sen5xType : uint8_t { SEN50, SEN54, SEN55, UNKNOWN };
|
||||
|
||||
struct GasTuning {
|
||||
uint16_t index_offset;
|
||||
uint16_t learning_time_offset_hours;
|
||||
@@ -51,6 +49,8 @@ class SEN5XComponent : public PollingComponent, public sensirion_common::Sensiri
|
||||
void dump_config() override;
|
||||
void update() override;
|
||||
|
||||
enum Sen5xType { SEN50, SEN54, SEN55, UNKNOWN };
|
||||
|
||||
void set_pm_1_0_sensor(sensor::Sensor *pm_1_0) { this->pm_1_0_sensor_ = pm_1_0; }
|
||||
void set_pm_2_5_sensor(sensor::Sensor *pm_2_5) { this->pm_2_5_sensor_ = pm_2_5; }
|
||||
void set_pm_4_0_sensor(sensor::Sensor *pm_4_0) { this->pm_4_0_sensor_ = pm_4_0; }
|
||||
@@ -102,12 +102,11 @@ class SEN5XComponent : public PollingComponent, public sensirion_common::Sensiri
|
||||
bool write_tuning_parameters_(uint16_t i2c_command, const GasTuning &tuning);
|
||||
bool write_temperature_compensation_(const TemperatureCompensation &compensation);
|
||||
|
||||
char serial_number_[17] = "UNKNOWN";
|
||||
uint16_t voc_baseline_state_[4]{0};
|
||||
uint32_t voc_baseline_time_;
|
||||
uint16_t firmware_version_;
|
||||
Sen5xType type_{Sen5xType::UNKNOWN};
|
||||
ERRORCODE error_code_;
|
||||
uint8_t serial_number_[4];
|
||||
bool initialized_{false};
|
||||
bool store_baseline_;
|
||||
|
||||
@@ -128,6 +127,7 @@ class SEN5XComponent : public PollingComponent, public sensirion_common::Sensiri
|
||||
optional<GasTuning> nox_tuning_params_;
|
||||
optional<TemperatureCompensation> temperature_compensation_;
|
||||
ESPPreferenceObject pref_;
|
||||
std::string product_name_;
|
||||
};
|
||||
|
||||
} // namespace sen5x
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace esphome {
|
||||
@@ -22,7 +21,7 @@ enum SmlMessageType : uint16_t { SML_PUBLIC_OPEN_RES = 0x0101, SML_GET_LIST_RES
|
||||
const uint16_t START_MASK = 0x55aa; // 0x1b 1b 1b 1b 01 01 01 01
|
||||
const uint16_t END_MASK = 0x0157; // 0x1b 1b 1b 1b 1a
|
||||
|
||||
constexpr std::array<uint8_t, 8> START_SEQ = {0x1b, 0x1b, 0x1b, 0x1b, 0x01, 0x01, 0x01, 0x01};
|
||||
const std::vector<uint8_t> START_SEQ = {0x1b, 0x1b, 0x1b, 0x1b, 0x01, 0x01, 0x01, 0x01};
|
||||
|
||||
} // namespace sml
|
||||
} // namespace esphome
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ["@linkedupbits"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
MULTI_CONF = True
|
||||
|
||||
CONF_SY6970_ID = "sy6970_id"
|
||||
CONF_ENABLE_STATUS_LED = "enable_status_led"
|
||||
CONF_INPUT_CURRENT_LIMIT = "input_current_limit"
|
||||
CONF_CHARGE_VOLTAGE = "charge_voltage"
|
||||
CONF_CHARGE_CURRENT = "charge_current"
|
||||
CONF_PRECHARGE_CURRENT = "precharge_current"
|
||||
CONF_CHARGE_ENABLED = "charge_enabled"
|
||||
CONF_ENABLE_ADC = "enable_adc"
|
||||
|
||||
sy6970_ns = cg.esphome_ns.namespace("sy6970")
|
||||
SY6970Component = sy6970_ns.class_(
|
||||
"SY6970Component", cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
SY6970Listener = sy6970_ns.class_("SY6970Listener")
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(SY6970Component),
|
||||
cv.Optional(CONF_ENABLE_STATUS_LED, default=True): cv.boolean,
|
||||
cv.Optional(CONF_INPUT_CURRENT_LIMIT, default=500): cv.int_range(
|
||||
min=100, max=3200
|
||||
),
|
||||
cv.Optional(CONF_CHARGE_VOLTAGE, default=4208): cv.int_range(
|
||||
min=3840, max=4608
|
||||
),
|
||||
cv.Optional(CONF_CHARGE_CURRENT, default=2048): cv.int_range(
|
||||
min=0, max=5056
|
||||
),
|
||||
cv.Optional(CONF_PRECHARGE_CURRENT, default=128): cv.int_range(
|
||||
min=64, max=1024
|
||||
),
|
||||
cv.Optional(CONF_CHARGE_ENABLED, default=True): cv.boolean,
|
||||
cv.Optional(CONF_ENABLE_ADC, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("5s"))
|
||||
.extend(i2c.i2c_device_schema(0x6A))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(
|
||||
config[CONF_ID],
|
||||
config[CONF_ENABLE_STATUS_LED],
|
||||
config[CONF_INPUT_CURRENT_LIMIT],
|
||||
config[CONF_CHARGE_VOLTAGE],
|
||||
config[CONF_CHARGE_CURRENT],
|
||||
config[CONF_PRECHARGE_CURRENT],
|
||||
config[CONF_CHARGE_ENABLED],
|
||||
config[CONF_ENABLE_ADC],
|
||||
)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
@@ -1,56 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import binary_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import DEVICE_CLASS_CONNECTIVITY, DEVICE_CLASS_POWER
|
||||
|
||||
from .. import CONF_SY6970_ID, SY6970Component, sy6970_ns
|
||||
|
||||
DEPENDENCIES = ["sy6970"]
|
||||
|
||||
CONF_VBUS_CONNECTED = "vbus_connected"
|
||||
CONF_CHARGING = "charging"
|
||||
CONF_CHARGE_DONE = "charge_done"
|
||||
|
||||
SY6970VbusConnectedBinarySensor = sy6970_ns.class_(
|
||||
"SY6970VbusConnectedBinarySensor", binary_sensor.BinarySensor
|
||||
)
|
||||
SY6970ChargingBinarySensor = sy6970_ns.class_(
|
||||
"SY6970ChargingBinarySensor", binary_sensor.BinarySensor
|
||||
)
|
||||
SY6970ChargeDoneBinarySensor = sy6970_ns.class_(
|
||||
"SY6970ChargeDoneBinarySensor", binary_sensor.BinarySensor
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_SY6970_ID): cv.use_id(SY6970Component),
|
||||
cv.Optional(CONF_VBUS_CONNECTED): binary_sensor.binary_sensor_schema(
|
||||
SY6970VbusConnectedBinarySensor,
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
),
|
||||
cv.Optional(CONF_CHARGING): binary_sensor.binary_sensor_schema(
|
||||
SY6970ChargingBinarySensor,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
),
|
||||
cv.Optional(CONF_CHARGE_DONE): binary_sensor.binary_sensor_schema(
|
||||
SY6970ChargeDoneBinarySensor,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
parent = await cg.get_variable(config[CONF_SY6970_ID])
|
||||
|
||||
if vbus_connected_config := config.get(CONF_VBUS_CONNECTED):
|
||||
sens = await binary_sensor.new_binary_sensor(vbus_connected_config)
|
||||
cg.add(parent.add_listener(sens))
|
||||
|
||||
if charging_config := config.get(CONF_CHARGING):
|
||||
sens = await binary_sensor.new_binary_sensor(charging_config)
|
||||
cg.add(parent.add_listener(sens))
|
||||
|
||||
if charge_done_config := config.get(CONF_CHARGE_DONE):
|
||||
sens = await binary_sensor.new_binary_sensor(charge_done_config)
|
||||
cg.add(parent.add_listener(sens))
|
||||
@@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../sy6970.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
|
||||
namespace esphome::sy6970 {
|
||||
|
||||
template<uint8_t REG, uint8_t SHIFT, uint8_t MASK, uint8_t TRUE_VALUE>
|
||||
class StatusBinarySensor : public SY6970Listener, public binary_sensor::BinarySensor {
|
||||
public:
|
||||
void on_data(const SY6970Data &data) override {
|
||||
uint8_t value = (data.registers[REG] >> SHIFT) & MASK;
|
||||
this->publish_state(value == TRUE_VALUE);
|
||||
}
|
||||
};
|
||||
|
||||
template<uint8_t REG, uint8_t SHIFT, uint8_t MASK, uint8_t FALSE_VALUE>
|
||||
class InverseStatusBinarySensor : public SY6970Listener, public binary_sensor::BinarySensor {
|
||||
public:
|
||||
void on_data(const SY6970Data &data) override {
|
||||
uint8_t value = (data.registers[REG] >> SHIFT) & MASK;
|
||||
this->publish_state(value != FALSE_VALUE);
|
||||
}
|
||||
};
|
||||
|
||||
// Custom binary sensor for charging (true when pre-charge or fast charge)
|
||||
class SY6970ChargingBinarySensor : public SY6970Listener, public binary_sensor::BinarySensor {
|
||||
public:
|
||||
void on_data(const SY6970Data &data) override {
|
||||
uint8_t chrg_stat = (data.registers[SY6970_REG_STATUS] >> 3) & 0x03;
|
||||
bool charging = chrg_stat != CHARGE_STATUS_NOT_CHARGING && chrg_stat != CHARGE_STATUS_CHARGE_DONE;
|
||||
this->publish_state(charging);
|
||||
}
|
||||
};
|
||||
|
||||
// Specialized sensor types using templates
|
||||
// VBUS connected: BUS_STATUS != NO_INPUT
|
||||
using SY6970VbusConnectedBinarySensor = InverseStatusBinarySensor<SY6970_REG_STATUS, 5, 0x07, BUS_STATUS_NO_INPUT>;
|
||||
|
||||
// Charge done: CHARGE_STATUS == CHARGE_DONE
|
||||
using SY6970ChargeDoneBinarySensor = StatusBinarySensor<SY6970_REG_STATUS, 3, 0x03, CHARGE_STATUS_CHARGE_DONE>;
|
||||
|
||||
} // namespace esphome::sy6970
|
||||
@@ -1,95 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_BATTERY_VOLTAGE,
|
||||
DEVICE_CLASS_CURRENT,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_MILLIAMP,
|
||||
UNIT_VOLT,
|
||||
)
|
||||
|
||||
from .. import CONF_SY6970_ID, SY6970Component, sy6970_ns
|
||||
|
||||
DEPENDENCIES = ["sy6970"]
|
||||
|
||||
CONF_VBUS_VOLTAGE = "vbus_voltage"
|
||||
CONF_SYSTEM_VOLTAGE = "system_voltage"
|
||||
CONF_CHARGE_CURRENT = "charge_current"
|
||||
CONF_PRECHARGE_CURRENT = "precharge_current"
|
||||
|
||||
SY6970VbusVoltageSensor = sy6970_ns.class_("SY6970VbusVoltageSensor", sensor.Sensor)
|
||||
SY6970BatteryVoltageSensor = sy6970_ns.class_(
|
||||
"SY6970BatteryVoltageSensor", sensor.Sensor
|
||||
)
|
||||
SY6970SystemVoltageSensor = sy6970_ns.class_("SY6970SystemVoltageSensor", sensor.Sensor)
|
||||
SY6970ChargeCurrentSensor = sy6970_ns.class_("SY6970ChargeCurrentSensor", sensor.Sensor)
|
||||
SY6970PrechargeCurrentSensor = sy6970_ns.class_(
|
||||
"SY6970PrechargeCurrentSensor", sensor.Sensor
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_SY6970_ID): cv.use_id(SY6970Component),
|
||||
cv.Optional(CONF_VBUS_VOLTAGE): sensor.sensor_schema(
|
||||
SY6970VbusVoltageSensor,
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
|
||||
SY6970BatteryVoltageSensor,
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_SYSTEM_VOLTAGE): sensor.sensor_schema(
|
||||
SY6970SystemVoltageSensor,
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CHARGE_CURRENT): sensor.sensor_schema(
|
||||
SY6970ChargeCurrentSensor,
|
||||
unit_of_measurement=UNIT_MILLIAMP,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_PRECHARGE_CURRENT): sensor.sensor_schema(
|
||||
SY6970PrechargeCurrentSensor,
|
||||
unit_of_measurement=UNIT_MILLIAMP,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
parent = await cg.get_variable(config[CONF_SY6970_ID])
|
||||
|
||||
if vbus_voltage_config := config.get(CONF_VBUS_VOLTAGE):
|
||||
sens = await sensor.new_sensor(vbus_voltage_config)
|
||||
cg.add(parent.add_listener(sens))
|
||||
|
||||
if battery_voltage_config := config.get(CONF_BATTERY_VOLTAGE):
|
||||
sens = await sensor.new_sensor(battery_voltage_config)
|
||||
cg.add(parent.add_listener(sens))
|
||||
|
||||
if system_voltage_config := config.get(CONF_SYSTEM_VOLTAGE):
|
||||
sens = await sensor.new_sensor(system_voltage_config)
|
||||
cg.add(parent.add_listener(sens))
|
||||
|
||||
if charge_current_config := config.get(CONF_CHARGE_CURRENT):
|
||||
sens = await sensor.new_sensor(charge_current_config)
|
||||
cg.add(parent.add_listener(sens))
|
||||
|
||||
if precharge_current_config := config.get(CONF_PRECHARGE_CURRENT):
|
||||
sens = await sensor.new_sensor(precharge_current_config)
|
||||
cg.add(parent.add_listener(sens))
|
||||
@@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../sy6970.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
namespace esphome::sy6970 {
|
||||
|
||||
// Template for voltage sensors (converts mV to V)
|
||||
template<uint8_t REG, uint8_t MASK, uint16_t BASE, uint16_t STEP>
|
||||
class VoltageSensor : public SY6970Listener, public sensor::Sensor {
|
||||
public:
|
||||
void on_data(const SY6970Data &data) override {
|
||||
uint8_t val = data.registers[REG] & MASK;
|
||||
uint16_t voltage_mv = BASE + (val * STEP);
|
||||
this->publish_state(voltage_mv * 0.001f); // Convert mV to V
|
||||
}
|
||||
};
|
||||
|
||||
// Template for current sensors (returns mA)
|
||||
template<uint8_t REG, uint8_t MASK, uint16_t BASE, uint16_t STEP>
|
||||
class CurrentSensor : public SY6970Listener, public sensor::Sensor {
|
||||
public:
|
||||
void on_data(const SY6970Data &data) override {
|
||||
uint8_t val = data.registers[REG] & MASK;
|
||||
uint16_t current_ma = BASE + (val * STEP);
|
||||
this->publish_state(current_ma);
|
||||
}
|
||||
};
|
||||
|
||||
// Specialized sensor types using templates
|
||||
using SY6970VbusVoltageSensor = VoltageSensor<SY6970_REG_VBUS_VOLTAGE, 0x7F, VBUS_BASE_MV, VBUS_STEP_MV>;
|
||||
using SY6970BatteryVoltageSensor = VoltageSensor<SY6970_REG_BATV, 0x7F, VBAT_BASE_MV, VBAT_STEP_MV>;
|
||||
using SY6970SystemVoltageSensor = VoltageSensor<SY6970_REG_VINDPM_STATUS, 0x7F, VSYS_BASE_MV, VSYS_STEP_MV>;
|
||||
using SY6970ChargeCurrentSensor = CurrentSensor<SY6970_REG_CHARGE_CURRENT_MONITOR, 0x7F, 0, CHG_CURRENT_STEP_MA>;
|
||||
|
||||
// Precharge current sensor needs special handling (bit shift)
|
||||
class SY6970PrechargeCurrentSensor : public SY6970Listener, public sensor::Sensor {
|
||||
public:
|
||||
void on_data(const SY6970Data &data) override {
|
||||
uint8_t iprechg = (data.registers[SY6970_REG_PRECHARGE_CURRENT] >> 4) & 0x0F;
|
||||
uint16_t iprechg_ma = PRE_CHG_BASE_MA + (iprechg * PRE_CHG_STEP_MA);
|
||||
this->publish_state(iprechg_ma);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace esphome::sy6970
|
||||
@@ -1,201 +0,0 @@
|
||||
#include "sy6970.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::sy6970 {
|
||||
|
||||
static const char *const TAG = "sy6970";
|
||||
|
||||
bool SY6970Component::read_all_registers_() {
|
||||
// Read all registers from 0x00 to 0x14 in one transaction (21 bytes)
|
||||
// This includes unused registers 0x0F, 0x10 for performance
|
||||
if (!this->read_bytes(SY6970_REG_INPUT_CURRENT_LIMIT, this->data_.registers, 21)) {
|
||||
ESP_LOGW(TAG, "Failed to read registers 0x00-0x14");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SY6970Component::write_register_(uint8_t reg, uint8_t value) {
|
||||
if (!this->write_byte(reg, value)) {
|
||||
ESP_LOGW(TAG, "Failed to write register 0x%02X", reg);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SY6970Component::update_register_(uint8_t reg, uint8_t mask, uint8_t value) {
|
||||
uint8_t reg_value;
|
||||
if (!this->read_byte(reg, ®_value)) {
|
||||
ESP_LOGW(TAG, "Failed to read register 0x%02X for update", reg);
|
||||
return false;
|
||||
}
|
||||
reg_value = (reg_value & ~mask) | (value & mask);
|
||||
return this->write_register_(reg, reg_value);
|
||||
}
|
||||
|
||||
void SY6970Component::setup() {
|
||||
ESP_LOGV(TAG, "Setting up SY6970...");
|
||||
|
||||
// Try to read chip ID
|
||||
uint8_t reg_value;
|
||||
if (!this->read_byte(SY6970_REG_DEVICE_ID, ®_value)) {
|
||||
ESP_LOGE(TAG, "Failed to communicate with SY6970");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t chip_id = reg_value & 0x03;
|
||||
if (chip_id != 0x00) {
|
||||
ESP_LOGW(TAG, "Unexpected chip ID: 0x%02X (expected 0x00)", chip_id);
|
||||
}
|
||||
|
||||
// Apply configuration options (all have defaults now)
|
||||
ESP_LOGV(TAG, "Setting LED enabled to %s", ONOFF(this->led_enabled_));
|
||||
this->set_led_enabled(this->led_enabled_);
|
||||
|
||||
ESP_LOGV(TAG, "Setting input current limit to %u mA", this->input_current_limit_);
|
||||
this->set_input_current_limit(this->input_current_limit_);
|
||||
|
||||
ESP_LOGV(TAG, "Setting charge voltage to %u mV", this->charge_voltage_);
|
||||
this->set_charge_target_voltage(this->charge_voltage_);
|
||||
|
||||
ESP_LOGV(TAG, "Setting charge current to %u mA", this->charge_current_);
|
||||
this->set_charge_current(this->charge_current_);
|
||||
|
||||
ESP_LOGV(TAG, "Setting precharge current to %u mA", this->precharge_current_);
|
||||
this->set_precharge_current(this->precharge_current_);
|
||||
|
||||
ESP_LOGV(TAG, "Setting charge enabled to %s", ONOFF(this->charge_enabled_));
|
||||
this->set_charge_enabled(this->charge_enabled_);
|
||||
|
||||
ESP_LOGV(TAG, "Setting ADC measurements to %s", ONOFF(this->enable_adc_));
|
||||
this->set_enable_adc_measure(this->enable_adc_);
|
||||
|
||||
ESP_LOGV(TAG, "SY6970 initialized successfully");
|
||||
}
|
||||
|
||||
void SY6970Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"SY6970:\n"
|
||||
" LED Enabled: %s\n"
|
||||
" Input Current Limit: %u mA\n"
|
||||
" Charge Voltage: %u mV\n"
|
||||
" Charge Current: %u mA\n"
|
||||
" Precharge Current: %u mA\n"
|
||||
" Charge Enabled: %s\n"
|
||||
" ADC Enabled: %s",
|
||||
ONOFF(this->led_enabled_), this->input_current_limit_, this->charge_voltage_, this->charge_current_,
|
||||
this->precharge_current_, ONOFF(this->charge_enabled_), ONOFF(this->enable_adc_));
|
||||
LOG_I2C_DEVICE(this);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Communication with SY6970 failed!");
|
||||
}
|
||||
}
|
||||
|
||||
void SY6970Component::update() {
|
||||
if (this->is_failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read all registers in one transaction
|
||||
if (!this->read_all_registers_()) {
|
||||
ESP_LOGW(TAG, "Failed to read registers during update");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
this->status_clear_warning();
|
||||
|
||||
// Notify all listeners with the new data
|
||||
for (auto *listener : this->listeners_) {
|
||||
listener->on_data(this->data_);
|
||||
}
|
||||
}
|
||||
|
||||
void SY6970Component::set_input_current_limit(uint16_t milliamps) {
|
||||
if (this->is_failed())
|
||||
return;
|
||||
|
||||
if (milliamps < INPUT_CURRENT_MIN) {
|
||||
milliamps = INPUT_CURRENT_MIN;
|
||||
}
|
||||
|
||||
uint8_t val = (milliamps - INPUT_CURRENT_MIN) / INPUT_CURRENT_STEP;
|
||||
if (val > 0x3F) {
|
||||
val = 0x3F;
|
||||
}
|
||||
|
||||
this->update_register_(SY6970_REG_INPUT_CURRENT_LIMIT, 0x3F, val);
|
||||
}
|
||||
|
||||
void SY6970Component::set_charge_target_voltage(uint16_t millivolts) {
|
||||
if (this->is_failed())
|
||||
return;
|
||||
|
||||
if (millivolts < CHG_VOLTAGE_BASE) {
|
||||
millivolts = CHG_VOLTAGE_BASE;
|
||||
}
|
||||
|
||||
uint8_t val = (millivolts - CHG_VOLTAGE_BASE) / CHG_VOLTAGE_STEP;
|
||||
if (val > 0x3F) {
|
||||
val = 0x3F;
|
||||
}
|
||||
|
||||
this->update_register_(SY6970_REG_CHARGE_VOLTAGE, 0xFC, val << 2);
|
||||
}
|
||||
|
||||
void SY6970Component::set_precharge_current(uint16_t milliamps) {
|
||||
if (this->is_failed())
|
||||
return;
|
||||
|
||||
if (milliamps < PRE_CHG_BASE_MA) {
|
||||
milliamps = PRE_CHG_BASE_MA;
|
||||
}
|
||||
|
||||
uint8_t val = (milliamps - PRE_CHG_BASE_MA) / PRE_CHG_STEP_MA;
|
||||
if (val > 0x0F) {
|
||||
val = 0x0F;
|
||||
}
|
||||
|
||||
this->update_register_(SY6970_REG_PRECHARGE_CURRENT, 0xF0, val << 4);
|
||||
}
|
||||
|
||||
void SY6970Component::set_charge_current(uint16_t milliamps) {
|
||||
if (this->is_failed())
|
||||
return;
|
||||
|
||||
uint8_t val = milliamps / 64;
|
||||
if (val > 0x7F) {
|
||||
val = 0x7F;
|
||||
}
|
||||
|
||||
this->update_register_(SY6970_REG_CHARGE_CURRENT, 0x7F, val);
|
||||
}
|
||||
|
||||
void SY6970Component::set_charge_enabled(bool enabled) {
|
||||
if (this->is_failed())
|
||||
return;
|
||||
|
||||
this->update_register_(SY6970_REG_SYS_CONTROL, 0x10, enabled ? 0x10 : 0x00);
|
||||
}
|
||||
|
||||
void SY6970Component::set_led_enabled(bool enabled) {
|
||||
if (this->is_failed())
|
||||
return;
|
||||
|
||||
// Bit 6: 0 = LED enabled, 1 = LED disabled
|
||||
this->update_register_(SY6970_REG_TIMER_CONTROL, 0x40, enabled ? 0x00 : 0x40);
|
||||
}
|
||||
|
||||
void SY6970Component::set_enable_adc_measure(bool enabled) {
|
||||
if (this->is_failed())
|
||||
return;
|
||||
|
||||
// Set bits to enable ADC conversion
|
||||
this->update_register_(SY6970_REG_ADC_CONTROL, 0xC0, enabled ? 0xC0 : 0x00);
|
||||
}
|
||||
|
||||
} // namespace esphome::sy6970
|
||||
@@ -1,122 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include <vector>
|
||||
|
||||
namespace esphome::sy6970 {
|
||||
|
||||
// SY6970 Register addresses with descriptive names
|
||||
static const uint8_t SY6970_REG_INPUT_CURRENT_LIMIT = 0x00; // Input current limit control
|
||||
static const uint8_t SY6970_REG_VINDPM = 0x01; // Input voltage limit
|
||||
static const uint8_t SY6970_REG_ADC_CONTROL = 0x02; // ADC control and function disable
|
||||
static const uint8_t SY6970_REG_SYS_CONTROL = 0x03; // Charge enable and system config
|
||||
static const uint8_t SY6970_REG_CHARGE_CURRENT = 0x04; // Fast charge current limit
|
||||
static const uint8_t SY6970_REG_PRECHARGE_CURRENT = 0x05; // Pre-charge/termination current
|
||||
static const uint8_t SY6970_REG_CHARGE_VOLTAGE = 0x06; // Charge voltage limit
|
||||
static const uint8_t SY6970_REG_TIMER_CONTROL = 0x07; // Charge timer and status LED control
|
||||
static const uint8_t SY6970_REG_IR_COMP = 0x08; // IR compensation
|
||||
static const uint8_t SY6970_REG_FORCE_DPDM = 0x09; // Force DPDM detection
|
||||
static const uint8_t SY6970_REG_BOOST_CONTROL = 0x0A; // Boost mode voltage/current
|
||||
static const uint8_t SY6970_REG_STATUS = 0x0B; // System status (bus, charge status)
|
||||
static const uint8_t SY6970_REG_FAULT = 0x0C; // Fault status (NTC)
|
||||
static const uint8_t SY6970_REG_VINDPM_STATUS = 0x0D; // Input voltage limit status (also sys voltage)
|
||||
static const uint8_t SY6970_REG_BATV = 0x0E; // Battery voltage
|
||||
static const uint8_t SY6970_REG_VBUS_VOLTAGE = 0x11; // VBUS voltage
|
||||
static const uint8_t SY6970_REG_CHARGE_CURRENT_MONITOR = 0x12; // Charge current
|
||||
static const uint8_t SY6970_REG_INPUT_VOLTAGE_LIMIT = 0x13; // Input voltage limit
|
||||
static const uint8_t SY6970_REG_DEVICE_ID = 0x14; // Part information
|
||||
|
||||
// Constants for voltage and current calculations
|
||||
static const uint16_t VBUS_BASE_MV = 2600; // mV
|
||||
static const uint16_t VBUS_STEP_MV = 100; // mV
|
||||
static const uint16_t VBAT_BASE_MV = 2304; // mV
|
||||
static const uint16_t VBAT_STEP_MV = 20; // mV
|
||||
static const uint16_t VSYS_BASE_MV = 2304; // mV
|
||||
static const uint16_t VSYS_STEP_MV = 20; // mV
|
||||
static const uint16_t CHG_CURRENT_STEP_MA = 50; // mA
|
||||
static const uint16_t PRE_CHG_BASE_MA = 64; // mA
|
||||
static const uint16_t PRE_CHG_STEP_MA = 64; // mA
|
||||
static const uint16_t CHG_VOLTAGE_BASE = 3840; // mV
|
||||
static const uint16_t CHG_VOLTAGE_STEP = 16; // mV
|
||||
static const uint16_t INPUT_CURRENT_MIN = 100; // mA
|
||||
static const uint16_t INPUT_CURRENT_STEP = 50; // mA
|
||||
|
||||
// Bus Status values (REG_0B[7:5])
|
||||
enum BusStatus {
|
||||
BUS_STATUS_NO_INPUT = 0,
|
||||
BUS_STATUS_USB_SDP = 1,
|
||||
BUS_STATUS_USB_CDP = 2,
|
||||
BUS_STATUS_USB_DCP = 3,
|
||||
BUS_STATUS_HVDCP = 4,
|
||||
BUS_STATUS_ADAPTER = 5,
|
||||
BUS_STATUS_NO_STD_ADAPTER = 6,
|
||||
BUS_STATUS_OTG = 7,
|
||||
};
|
||||
|
||||
// Charge Status values (REG_0B[4:3])
|
||||
enum ChargeStatus {
|
||||
CHARGE_STATUS_NOT_CHARGING = 0,
|
||||
CHARGE_STATUS_PRE_CHARGE = 1,
|
||||
CHARGE_STATUS_FAST_CHARGE = 2,
|
||||
CHARGE_STATUS_CHARGE_DONE = 3,
|
||||
};
|
||||
|
||||
// Structure to hold all register data read in one transaction
|
||||
struct SY6970Data {
|
||||
uint8_t registers[21]; // Registers 0x00-0x14 (includes unused 0x0F, 0x10)
|
||||
};
|
||||
|
||||
// Listener interface for components that want to receive SY6970 data updates
|
||||
class SY6970Listener {
|
||||
public:
|
||||
virtual void on_data(const SY6970Data &data) = 0;
|
||||
};
|
||||
|
||||
class SY6970Component : public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
SY6970Component(bool led_enabled, uint16_t input_current_limit, uint16_t charge_voltage, uint16_t charge_current,
|
||||
uint16_t precharge_current, bool charge_enabled, bool enable_adc)
|
||||
: led_enabled_(led_enabled),
|
||||
input_current_limit_(input_current_limit),
|
||||
charge_voltage_(charge_voltage),
|
||||
charge_current_(charge_current),
|
||||
precharge_current_(precharge_current),
|
||||
charge_enabled_(charge_enabled),
|
||||
enable_adc_(enable_adc) {}
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void update() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
// Listener registration
|
||||
void add_listener(SY6970Listener *listener) { this->listeners_.push_back(listener); }
|
||||
|
||||
// Configuration methods to be called from lambdas
|
||||
void set_input_current_limit(uint16_t milliamps);
|
||||
void set_charge_target_voltage(uint16_t millivolts);
|
||||
void set_precharge_current(uint16_t milliamps);
|
||||
void set_charge_current(uint16_t milliamps);
|
||||
void set_charge_enabled(bool enabled);
|
||||
void set_led_enabled(bool enabled);
|
||||
void set_enable_adc_measure(bool enabled = true);
|
||||
|
||||
protected:
|
||||
bool read_all_registers_();
|
||||
bool write_register_(uint8_t reg, uint8_t value);
|
||||
bool update_register_(uint8_t reg, uint8_t mask, uint8_t value);
|
||||
|
||||
SY6970Data data_{};
|
||||
std::vector<SY6970Listener *> listeners_;
|
||||
|
||||
// Configuration values to set during setup()
|
||||
bool led_enabled_;
|
||||
uint16_t input_current_limit_;
|
||||
uint16_t charge_voltage_;
|
||||
uint16_t charge_current_;
|
||||
uint16_t precharge_current_;
|
||||
bool charge_enabled_;
|
||||
bool enable_adc_;
|
||||
};
|
||||
|
||||
} // namespace esphome::sy6970
|
||||
@@ -1,52 +0,0 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import text_sensor
|
||||
import esphome.config_validation as cv
|
||||
|
||||
from .. import CONF_SY6970_ID, SY6970Component, sy6970_ns
|
||||
|
||||
DEPENDENCIES = ["sy6970"]
|
||||
|
||||
CONF_BUS_STATUS = "bus_status"
|
||||
CONF_CHARGE_STATUS = "charge_status"
|
||||
CONF_NTC_STATUS = "ntc_status"
|
||||
|
||||
SY6970BusStatusTextSensor = sy6970_ns.class_(
|
||||
"SY6970BusStatusTextSensor", text_sensor.TextSensor
|
||||
)
|
||||
SY6970ChargeStatusTextSensor = sy6970_ns.class_(
|
||||
"SY6970ChargeStatusTextSensor", text_sensor.TextSensor
|
||||
)
|
||||
SY6970NtcStatusTextSensor = sy6970_ns.class_(
|
||||
"SY6970NtcStatusTextSensor", text_sensor.TextSensor
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_SY6970_ID): cv.use_id(SY6970Component),
|
||||
cv.Optional(CONF_BUS_STATUS): text_sensor.text_sensor_schema(
|
||||
SY6970BusStatusTextSensor
|
||||
),
|
||||
cv.Optional(CONF_CHARGE_STATUS): text_sensor.text_sensor_schema(
|
||||
SY6970ChargeStatusTextSensor
|
||||
),
|
||||
cv.Optional(CONF_NTC_STATUS): text_sensor.text_sensor_schema(
|
||||
SY6970NtcStatusTextSensor
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
parent = await cg.get_variable(config[CONF_SY6970_ID])
|
||||
|
||||
if bus_status_config := config.get(CONF_BUS_STATUS):
|
||||
sens = await text_sensor.new_text_sensor(bus_status_config)
|
||||
cg.add(parent.add_listener(sens))
|
||||
|
||||
if charge_status_config := config.get(CONF_CHARGE_STATUS):
|
||||
sens = await text_sensor.new_text_sensor(charge_status_config)
|
||||
cg.add(parent.add_listener(sens))
|
||||
|
||||
if ntc_status_config := config.get(CONF_NTC_STATUS):
|
||||
sens = await text_sensor.new_text_sensor(ntc_status_config)
|
||||
cg.add(parent.add_listener(sens))
|
||||
@@ -1,96 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../sy6970.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
|
||||
namespace esphome::sy6970 {
|
||||
|
||||
// Bus status text sensor
|
||||
class SY6970BusStatusTextSensor : public SY6970Listener, public text_sensor::TextSensor {
|
||||
public:
|
||||
void on_data(const SY6970Data &data) override {
|
||||
uint8_t status = (data.registers[SY6970_REG_STATUS] >> 5) & 0x07;
|
||||
const char *status_str = this->get_bus_status_string_(status);
|
||||
this->publish_state(status_str);
|
||||
}
|
||||
|
||||
protected:
|
||||
const char *get_bus_status_string_(uint8_t status) {
|
||||
switch (status) {
|
||||
case BUS_STATUS_NO_INPUT:
|
||||
return "No Input";
|
||||
case BUS_STATUS_USB_SDP:
|
||||
return "USB SDP";
|
||||
case BUS_STATUS_USB_CDP:
|
||||
return "USB CDP";
|
||||
case BUS_STATUS_USB_DCP:
|
||||
return "USB DCP";
|
||||
case BUS_STATUS_HVDCP:
|
||||
return "HVDCP";
|
||||
case BUS_STATUS_ADAPTER:
|
||||
return "Adapter";
|
||||
case BUS_STATUS_NO_STD_ADAPTER:
|
||||
return "Non-Standard Adapter";
|
||||
case BUS_STATUS_OTG:
|
||||
return "OTG";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Charge status text sensor
|
||||
class SY6970ChargeStatusTextSensor : public SY6970Listener, public text_sensor::TextSensor {
|
||||
public:
|
||||
void on_data(const SY6970Data &data) override {
|
||||
uint8_t status = (data.registers[SY6970_REG_STATUS] >> 3) & 0x03;
|
||||
const char *status_str = this->get_charge_status_string_(status);
|
||||
this->publish_state(status_str);
|
||||
}
|
||||
|
||||
protected:
|
||||
const char *get_charge_status_string_(uint8_t status) {
|
||||
switch (status) {
|
||||
case CHARGE_STATUS_NOT_CHARGING:
|
||||
return "Not Charging";
|
||||
case CHARGE_STATUS_PRE_CHARGE:
|
||||
return "Pre-charge";
|
||||
case CHARGE_STATUS_FAST_CHARGE:
|
||||
return "Fast Charge";
|
||||
case CHARGE_STATUS_CHARGE_DONE:
|
||||
return "Charge Done";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// NTC status text sensor
|
||||
class SY6970NtcStatusTextSensor : public SY6970Listener, public text_sensor::TextSensor {
|
||||
public:
|
||||
void on_data(const SY6970Data &data) override {
|
||||
uint8_t status = data.registers[SY6970_REG_FAULT] & 0x07;
|
||||
const char *status_str = this->get_ntc_status_string_(status);
|
||||
this->publish_state(status_str);
|
||||
}
|
||||
|
||||
protected:
|
||||
const char *get_ntc_status_string_(uint8_t status) {
|
||||
switch (status) {
|
||||
case 0:
|
||||
return "Normal";
|
||||
case 2:
|
||||
return "Warm";
|
||||
case 3:
|
||||
return "Cool";
|
||||
case 5:
|
||||
return "Cold";
|
||||
case 6:
|
||||
return "Hot";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace esphome::sy6970
|
||||
@@ -20,7 +20,7 @@ from .. import template_ns
|
||||
CONF_CURRENT_TEMPERATURE = "current_temperature"
|
||||
|
||||
TemplateWaterHeater = template_ns.class_(
|
||||
"TemplateWaterHeater", cg.Component, water_heater.WaterHeater
|
||||
"TemplateWaterHeater", water_heater.WaterHeater
|
||||
)
|
||||
|
||||
TemplateWaterHeaterPublishAction = template_ns.class_(
|
||||
@@ -36,29 +36,24 @@ RESTORE_MODES = {
|
||||
"RESTORE_AND_CALL": TemplateWaterHeaterRestoreMode.WATER_HEATER_RESTORE_AND_CALL,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
water_heater.water_heater_schema(TemplateWaterHeater)
|
||||
.extend(
|
||||
{
|
||||
cv.Optional(CONF_OPTIMISTIC, default=True): cv.boolean,
|
||||
cv.Optional(CONF_SET_ACTION): automation.validate_automation(single=True),
|
||||
cv.Optional(CONF_RESTORE_MODE, default="NO_RESTORE"): cv.enum(
|
||||
RESTORE_MODES, upper=True
|
||||
),
|
||||
cv.Optional(CONF_CURRENT_TEMPERATURE): cv.returning_lambda,
|
||||
cv.Optional(CONF_MODE): cv.returning_lambda,
|
||||
cv.Optional(CONF_SUPPORTED_MODES): cv.ensure_list(
|
||||
water_heater.validate_water_heater_mode
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
CONFIG_SCHEMA = water_heater.water_heater_schema(TemplateWaterHeater).extend(
|
||||
{
|
||||
cv.Optional(CONF_OPTIMISTIC, default=True): cv.boolean,
|
||||
cv.Optional(CONF_SET_ACTION): automation.validate_automation(single=True),
|
||||
cv.Optional(CONF_RESTORE_MODE, default="NO_RESTORE"): cv.enum(
|
||||
RESTORE_MODES, upper=True
|
||||
),
|
||||
cv.Optional(CONF_CURRENT_TEMPERATURE): cv.returning_lambda,
|
||||
cv.Optional(CONF_MODE): cv.returning_lambda,
|
||||
cv.Optional(CONF_SUPPORTED_MODES): cv.ensure_list(
|
||||
water_heater.validate_water_heater_mode
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await water_heater.register_water_heater(var, config)
|
||||
|
||||
cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
|
||||
|
||||
@@ -10,7 +10,7 @@ TemplateWaterHeater::TemplateWaterHeater() : set_trigger_(new Trigger<>()) {}
|
||||
void TemplateWaterHeater::setup() {
|
||||
if (this->restore_mode_ == TemplateWaterHeaterRestoreMode::WATER_HEATER_RESTORE ||
|
||||
this->restore_mode_ == TemplateWaterHeaterRestoreMode::WATER_HEATER_RESTORE_AND_CALL) {
|
||||
auto restore = this->restore_state_();
|
||||
auto restore = this->restore_state();
|
||||
|
||||
if (restore.has_value()) {
|
||||
restore->perform();
|
||||
|
||||
@@ -13,7 +13,7 @@ enum TemplateWaterHeaterRestoreMode {
|
||||
WATER_HEATER_RESTORE_AND_CALL,
|
||||
};
|
||||
|
||||
class TemplateWaterHeater : public Component, public water_heater::WaterHeater {
|
||||
class TemplateWaterHeater : public water_heater::WaterHeater {
|
||||
public:
|
||||
TemplateWaterHeater();
|
||||
|
||||
|
||||
@@ -35,6 +35,9 @@ void TM1638Component::setup() {
|
||||
this->set_intensity(intensity_);
|
||||
|
||||
this->reset_(); // all LEDs off
|
||||
|
||||
for (uint8_t i = 0; i < 8; i++) // zero fill print buffer
|
||||
this->buffer_[i] = 0;
|
||||
}
|
||||
|
||||
void TM1638Component::dump_config() {
|
||||
|
||||
@@ -70,7 +70,7 @@ class TM1638Component : public PollingComponent {
|
||||
GPIOPin *clk_pin_;
|
||||
GPIOPin *stb_pin_;
|
||||
GPIOPin *dio_pin_;
|
||||
uint8_t buffer_[8]{};
|
||||
uint8_t *buffer_ = new uint8_t[8];
|
||||
tm1638_writer_t writer_{};
|
||||
std::vector<KeyListener *> listeners_{};
|
||||
};
|
||||
|
||||
@@ -1,23 +1,11 @@
|
||||
#include "uln2003.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::uln2003 {
|
||||
namespace esphome {
|
||||
namespace uln2003 {
|
||||
|
||||
static const char *const TAG = "uln2003.stepper";
|
||||
|
||||
static const LogString *step_mode_to_log_string(ULN2003StepMode mode) {
|
||||
switch (mode) {
|
||||
case ULN2003_STEP_MODE_FULL_STEP:
|
||||
return LOG_STR("FULL STEP");
|
||||
case ULN2003_STEP_MODE_HALF_STEP:
|
||||
return LOG_STR("HALF STEP");
|
||||
case ULN2003_STEP_MODE_WAVE_DRIVE:
|
||||
return LOG_STR("WAVE DRIVE");
|
||||
default:
|
||||
return LOG_STR("UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
void ULN2003::setup() {
|
||||
this->pin_a_->setup();
|
||||
this->pin_b_->setup();
|
||||
@@ -54,7 +42,22 @@ void ULN2003::dump_config() {
|
||||
LOG_PIN(" Pin B: ", this->pin_b_);
|
||||
LOG_PIN(" Pin C: ", this->pin_c_);
|
||||
LOG_PIN(" Pin D: ", this->pin_d_);
|
||||
ESP_LOGCONFIG(TAG, " Step Mode: %s", LOG_STR_ARG(step_mode_to_log_string(this->step_mode_)));
|
||||
const char *step_mode_s;
|
||||
switch (this->step_mode_) {
|
||||
case ULN2003_STEP_MODE_FULL_STEP:
|
||||
step_mode_s = "FULL STEP";
|
||||
break;
|
||||
case ULN2003_STEP_MODE_HALF_STEP:
|
||||
step_mode_s = "HALF STEP";
|
||||
break;
|
||||
case ULN2003_STEP_MODE_WAVE_DRIVE:
|
||||
step_mode_s = "WAVE DRIVE";
|
||||
break;
|
||||
default:
|
||||
step_mode_s = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Step Mode: %s", step_mode_s);
|
||||
}
|
||||
void ULN2003::write_step_(int32_t step) {
|
||||
int32_t n = this->step_mode_ == ULN2003_STEP_MODE_HALF_STEP ? 8 : 4;
|
||||
@@ -87,4 +90,5 @@ void ULN2003::write_step_(int32_t step) {
|
||||
this->pin_d_->digital_write((res >> 3) & 1);
|
||||
}
|
||||
|
||||
} // namespace esphome::uln2003
|
||||
} // namespace uln2003
|
||||
} // namespace esphome
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/stepper/stepper.h"
|
||||
|
||||
namespace esphome::uln2003 {
|
||||
namespace esphome {
|
||||
namespace uln2003 {
|
||||
|
||||
enum ULN2003StepMode {
|
||||
ULN2003_STEP_MODE_FULL_STEP,
|
||||
@@ -39,4 +40,5 @@ class ULN2003 : public stepper::Stepper, public Component {
|
||||
int32_t current_uln_pos_{0};
|
||||
};
|
||||
|
||||
} // namespace esphome::uln2003
|
||||
} // namespace uln2003
|
||||
} // namespace esphome
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
|
||||
#include <strings.h>
|
||||
|
||||
namespace esphome {
|
||||
@@ -40,13 +38,13 @@ Valve::Valve() : position{VALVE_OPEN} {}
|
||||
|
||||
ValveCall::ValveCall(Valve *parent) : parent_(parent) {}
|
||||
ValveCall &ValveCall::set_command(const char *command) {
|
||||
if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("OPEN")) == 0) {
|
||||
if (strcasecmp(command, "OPEN") == 0) {
|
||||
this->set_command_open();
|
||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("CLOSE")) == 0) {
|
||||
} else if (strcasecmp(command, "CLOSE") == 0) {
|
||||
this->set_command_close();
|
||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("STOP")) == 0) {
|
||||
} else if (strcasecmp(command, "STOP") == 0) {
|
||||
this->set_command_stop();
|
||||
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("TOGGLE")) == 0) {
|
||||
} else if (strcasecmp(command, "TOGGLE") == 0) {
|
||||
this->set_command_toggle();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command);
|
||||
|
||||
@@ -18,7 +18,7 @@ CODEOWNERS = ["@dhoeben"]
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
water_heater_ns = cg.esphome_ns.namespace("water_heater")
|
||||
WaterHeater = water_heater_ns.class_("WaterHeater", cg.EntityBase)
|
||||
WaterHeater = water_heater_ns.class_("WaterHeater", cg.EntityBase, cg.Component)
|
||||
WaterHeaterCall = water_heater_ns.class_("WaterHeaterCall")
|
||||
WaterHeaterTraits = water_heater_ns.class_("WaterHeaterTraits")
|
||||
|
||||
@@ -46,7 +46,7 @@ _WATER_HEATER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
_WATER_HEATER_SCHEMA.add_extra(entity_duplicate_validator("water_heater"))
|
||||
|
||||
@@ -91,6 +91,8 @@ async def register_water_heater(var: cg.Pvariable, config: ConfigType) -> cg.Pva
|
||||
|
||||
cg.add_define("USE_WATER_HEATER")
|
||||
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(cg.App.register_water_heater(var))
|
||||
|
||||
CORE.register_platform_component("water_heater", var)
|
||||
|
||||
@@ -146,6 +146,10 @@ void WaterHeaterCall::validate_() {
|
||||
}
|
||||
}
|
||||
|
||||
void WaterHeater::setup() {
|
||||
this->pref_ = global_preferences->make_preference<SavedWaterHeaterState>(this->get_preference_hash());
|
||||
}
|
||||
|
||||
void WaterHeater::publish_state() {
|
||||
auto traits = this->get_traits();
|
||||
ESP_LOGD(TAG,
|
||||
@@ -184,8 +188,7 @@ void WaterHeater::publish_state() {
|
||||
this->pref_.save(&saved);
|
||||
}
|
||||
|
||||
optional<WaterHeaterCall> WaterHeater::restore_state_() {
|
||||
this->pref_ = global_preferences->make_preference<SavedWaterHeaterState>(this->get_preference_hash());
|
||||
optional<WaterHeaterCall> WaterHeater::restore_state() {
|
||||
SavedWaterHeaterState recovered{};
|
||||
if (!this->pref_.load(&recovered))
|
||||
return {};
|
||||
|
||||
@@ -177,7 +177,7 @@ class WaterHeaterTraits {
|
||||
WaterHeaterModeMask supported_modes_;
|
||||
};
|
||||
|
||||
class WaterHeater : public EntityBase {
|
||||
class WaterHeater : public EntityBase, public Component {
|
||||
public:
|
||||
WaterHeaterMode get_mode() const { return this->mode_; }
|
||||
float get_current_temperature() const { return this->current_temperature_; }
|
||||
@@ -204,15 +204,16 @@ class WaterHeater : public EntityBase {
|
||||
#endif
|
||||
virtual void control(const WaterHeaterCall &call) = 0;
|
||||
|
||||
void setup() override;
|
||||
|
||||
optional<WaterHeaterCall> restore_state();
|
||||
|
||||
protected:
|
||||
virtual WaterHeaterTraits traits() = 0;
|
||||
|
||||
/// Log the traits of this water heater for dump_config().
|
||||
void dump_traits_(const char *tag);
|
||||
|
||||
/// Restore the state of the water heater, call this from your setup() method.
|
||||
optional<WaterHeaterCall> restore_state_();
|
||||
|
||||
/// Set the mode of the water heater. Should only be called from control().
|
||||
void set_mode_(WaterHeaterMode mode) { this->mode_ = mode; }
|
||||
/// Set the target temperature of the water heater. Should only be called from control().
|
||||
|
||||
@@ -698,10 +698,6 @@ bool WiFiComponent::wifi_scan_start_(bool passive) {
|
||||
if (!this->wifi_mode_(true, {}))
|
||||
return false;
|
||||
|
||||
// Reset scan_done_ before starting new scan to prevent stale flag from previous scan
|
||||
// (e.g., roaming scan completed just before unexpected disconnect)
|
||||
this->scan_done_ = false;
|
||||
|
||||
struct scan_config config {};
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.ssid = nullptr;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#ifdef USE_WIFI_WPA2_EAP
|
||||
#if (ESP_IDF_VERSION_MAJOR >= 5) && (ESP_IDF_VERSION_MINOR >= 1)
|
||||
@@ -829,29 +828,16 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) {
|
||||
|
||||
uint16_t number = it.number;
|
||||
scan_result_.init(number);
|
||||
#ifdef USE_ESP32_HOSTED
|
||||
// getting records one at a time fails on P4 with hosted esp32 WiFi coprocessor
|
||||
// Presumably an upstream bug, work-around by getting all records at once
|
||||
auto records = std::make_unique<wifi_ap_record_t[]>(number);
|
||||
err = esp_wifi_scan_get_ap_records(&number, records.get());
|
||||
if (err != ESP_OK) {
|
||||
esp_wifi_clear_ap_list();
|
||||
ESP_LOGW(TAG, "esp_wifi_scan_get_ap_records failed: %s", esp_err_to_name(err));
|
||||
return;
|
||||
}
|
||||
for (uint16_t i = 0; i < number; i++) {
|
||||
wifi_ap_record_t &record = records[i];
|
||||
#else
|
||||
|
||||
// Process one record at a time to avoid large buffer allocation
|
||||
wifi_ap_record_t record;
|
||||
for (uint16_t i = 0; i < number; i++) {
|
||||
wifi_ap_record_t record;
|
||||
err = esp_wifi_scan_get_ap_record(&record);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "esp_wifi_scan_get_ap_record failed: %s", esp_err_to_name(err));
|
||||
esp_wifi_clear_ap_list(); // Free remaining records not yet retrieved
|
||||
break;
|
||||
}
|
||||
#endif // USE_ESP32_HOSTED
|
||||
bssid_t bssid;
|
||||
std::copy(record.bssid, record.bssid + 6, bssid.begin());
|
||||
std::string ssid(reinterpret_cast<const char *>(record.ssid));
|
||||
|
||||
@@ -649,10 +649,6 @@ bool WiFiComponent::wifi_scan_start_(bool passive) {
|
||||
if (!this->wifi_mode_(true, {}))
|
||||
return false;
|
||||
|
||||
// Reset scan_done_ before starting new scan to prevent stale flag from previous scan
|
||||
// (e.g., roaming scan completed just before unexpected disconnect)
|
||||
this->scan_done_ = false;
|
||||
|
||||
// need to use WiFi because of WiFiScanClass allocations :(
|
||||
int16_t err = WiFi.scanNetworks(true, true, passive, 200);
|
||||
if (err != WIFI_SCAN_RUNNING) {
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
#define USE_DEVICES
|
||||
#define USE_DISPLAY
|
||||
#define USE_ENTITY_ICON
|
||||
#define USE_ESP32_HOSTED
|
||||
#define USE_ESP32_IMPROV_STATE_CALLBACK
|
||||
#define USE_EVENT
|
||||
#define USE_FAN
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/progmem.h"
|
||||
#include "esphome/core/string_ref.h"
|
||||
|
||||
#include <strings.h>
|
||||
@@ -452,15 +451,15 @@ std::string format_bin(const uint8_t *data, size_t length) {
|
||||
}
|
||||
|
||||
ParseOnOffState parse_on_off(const char *str, const char *on, const char *off) {
|
||||
if (on == nullptr && ESPHOME_strcasecmp_P(str, ESPHOME_PSTR("on")) == 0)
|
||||
if (on == nullptr && strcasecmp(str, "on") == 0)
|
||||
return PARSE_ON;
|
||||
if (on != nullptr && strcasecmp(str, on) == 0)
|
||||
return PARSE_ON;
|
||||
if (off == nullptr && ESPHOME_strcasecmp_P(str, ESPHOME_PSTR("off")) == 0)
|
||||
if (off == nullptr && strcasecmp(str, "off") == 0)
|
||||
return PARSE_OFF;
|
||||
if (off != nullptr && strcasecmp(str, off) == 0)
|
||||
return PARSE_OFF;
|
||||
if (ESPHOME_strcasecmp_P(str, ESPHOME_PSTR("toggle")) == 0)
|
||||
if (strcasecmp(str, "toggle") == 0)
|
||||
return PARSE_TOGGLE;
|
||||
|
||||
return PARSE_NONE;
|
||||
|
||||
@@ -148,6 +148,25 @@ template<typename T, size_t N> class StaticVector {
|
||||
size_t count_{0};
|
||||
|
||||
public:
|
||||
// Default constructor
|
||||
StaticVector() = default;
|
||||
|
||||
// Iterator range constructor
|
||||
template<typename InputIt> StaticVector(InputIt first, InputIt last) {
|
||||
while (first != last && count_ < N) {
|
||||
data_[count_++] = *first++;
|
||||
}
|
||||
}
|
||||
|
||||
// Initializer list constructor
|
||||
StaticVector(std::initializer_list<T> init) {
|
||||
for (const auto &val : init) {
|
||||
if (count_ >= N)
|
||||
break;
|
||||
data_[count_++] = val;
|
||||
}
|
||||
}
|
||||
|
||||
// Minimal vector-compatible interface - only what we actually use
|
||||
void push_back(const T &value) {
|
||||
if (count_ < N) {
|
||||
@@ -155,6 +174,17 @@ template<typename T, size_t N> class StaticVector {
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all elements
|
||||
void clear() { count_ = 0; }
|
||||
|
||||
// Assign from iterator range
|
||||
template<typename InputIt> void assign(InputIt first, InputIt last) {
|
||||
count_ = 0;
|
||||
while (first != last && count_ < N) {
|
||||
data_[count_++] = *first++;
|
||||
}
|
||||
}
|
||||
|
||||
// Return reference to next element and increment count (with bounds checking)
|
||||
T &emplace_next() {
|
||||
if (count_ >= N) {
|
||||
@@ -186,6 +216,10 @@ template<typename T, size_t N> class StaticVector {
|
||||
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
|
||||
const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
|
||||
|
||||
// Conversion to std::span for compatibility with span-based APIs
|
||||
operator std::span<T>() { return std::span<T>(data_.data(), count_); }
|
||||
operator std::span<const T>() const { return std::span<const T>(data_.data(), count_); }
|
||||
};
|
||||
|
||||
/// Fixed-capacity vector - allocates once at runtime, never reallocates
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
#define ESPHOME_strncpy_P strncpy_P
|
||||
#define ESPHOME_strncat_P strncat_P
|
||||
#define ESPHOME_snprintf_P snprintf_P
|
||||
#define ESPHOME_strcmp_P strcmp_P
|
||||
#define ESPHOME_strcasecmp_P strcasecmp_P
|
||||
#define ESPHOME_strncmp_P strncmp_P
|
||||
#define ESPHOME_strncasecmp_P strncasecmp_P
|
||||
// Type for pointers to PROGMEM strings (for use with ESPHOME_F return values)
|
||||
using ProgmemStr = const __FlashStringHelper *;
|
||||
#else
|
||||
@@ -25,10 +21,6 @@ using ProgmemStr = const __FlashStringHelper *;
|
||||
#define ESPHOME_strncpy_P strncpy
|
||||
#define ESPHOME_strncat_P strncat
|
||||
#define ESPHOME_snprintf_P snprintf
|
||||
#define ESPHOME_strcmp_P strcmp
|
||||
#define ESPHOME_strcasecmp_P strcasecmp
|
||||
#define ESPHOME_strncmp_P strncmp
|
||||
#define ESPHOME_strncasecmp_P strncasecmp
|
||||
// Type for pointers to strings (no PROGMEM on non-ESP8266 platforms)
|
||||
using ProgmemStr = const char *;
|
||||
#endif
|
||||
|
||||
@@ -154,12 +154,6 @@ def check_error(data: list[int] | bytes, expect: int | list[int] | None) -> None
|
||||
"""
|
||||
if not expect:
|
||||
return
|
||||
if not data:
|
||||
raise OTAError(
|
||||
"Error: Device closed connection without responding. "
|
||||
"This may indicate the device ran out of memory, "
|
||||
"a network issue, or the connection was interrupted."
|
||||
)
|
||||
dat = data[0]
|
||||
if dat == RESPONSE_ERROR_MAGIC:
|
||||
raise OTAError("Error: Invalid magic byte")
|
||||
|
||||
@@ -212,7 +212,7 @@ build_unflags =
|
||||
; This are common settings for the LibreTiny (all variants) using Arduino.
|
||||
[common:libretiny-arduino]
|
||||
extends = common:arduino
|
||||
platform = https://github.com/libretiny-eu/libretiny.git#v1.11.0
|
||||
platform = libretiny@1.9.2
|
||||
framework = arduino
|
||||
lib_compat_mode = soft
|
||||
lib_deps =
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[build-system]
|
||||
requires = ["setuptools==80.10.2", "wheel>=0.43,<0.47"]
|
||||
requires = ["setuptools==80.10.1", "wheel>=0.43,<0.47"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
|
||||
@@ -12,7 +12,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
|
||||
esptool==5.1.0
|
||||
click==8.1.7
|
||||
esphome-dashboard==20260110.0
|
||||
aioesphomeapi==43.14.0
|
||||
aioesphomeapi==43.13.0
|
||||
zeroconf==0.148.0
|
||||
puremagic==1.30
|
||||
ruamel.yaml==0.19.1 # dashboard_import
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
substitutions:
|
||||
verify_ssl: "true"
|
||||
|
||||
http_request:
|
||||
ca_certificate_path: $component_dir/test_ca.pem
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -1,10 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBkTCB+wIJAKHBfpegPjMCMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBnVu
|
||||
dXNlZDAeFw0yNDAxMDEwMDAwMDBaFw0yNTAxMDEwMDAwMDBaMBExDzANBgNVBAMM
|
||||
BnVudXNlZDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC5mMUB1hOgLmlnXtsvcGMP
|
||||
XkhAqZaR0dDPW5OS8VEopWLJCX9Y0cvNCqiDI8cnP8pP8XJGU1hGLvA5PJzWnWZz
|
||||
AgMBAAGjUzBRMB0GA1UdDgQWBBR5oQ9KqFeZOdBuAJrXxEP0dqzPtTAfBgNVHSME
|
||||
GDAWgBR5oQ9KqFeZOdBuAJrXxEP0dqzPtTAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
|
||||
SIb3DQEBCwUAA0EAKqZFf6+f8FPDbKyPCpssquojgn7fEXqr/I/yz0R5CowGdMms
|
||||
H3WH3aKP4lLSHdPTBtfIoJi3gEIZjFxp3S1TWw==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,57 +0,0 @@
|
||||
sy6970:
|
||||
id: sy6970_component
|
||||
i2c_id: i2c_bus
|
||||
address: 0x6A
|
||||
enable_status_led: true
|
||||
input_current_limit: 1000
|
||||
charge_voltage: 4200
|
||||
charge_current: 500
|
||||
precharge_current: 128
|
||||
charge_enabled: true
|
||||
enable_adc: true
|
||||
update_interval: 5s
|
||||
|
||||
sensor:
|
||||
- platform: sy6970
|
||||
sy6970_id: sy6970_component
|
||||
vbus_voltage:
|
||||
name: "VBUS Voltage"
|
||||
id: vbus_voltage_sensor
|
||||
battery_voltage:
|
||||
name: "Battery Voltage"
|
||||
id: battery_voltage_sensor
|
||||
system_voltage:
|
||||
name: "System Voltage"
|
||||
id: system_voltage_sensor
|
||||
charge_current:
|
||||
name: "Charge Current"
|
||||
id: charge_current_sensor
|
||||
precharge_current:
|
||||
name: "Precharge Current"
|
||||
id: precharge_current_sensor
|
||||
|
||||
binary_sensor:
|
||||
- platform: sy6970
|
||||
sy6970_id: sy6970_component
|
||||
vbus_connected:
|
||||
name: "VBUS Connected"
|
||||
id: vbus_connected_binary
|
||||
charging:
|
||||
name: "Charging"
|
||||
id: charging_binary
|
||||
charge_done:
|
||||
name: "Charge Done"
|
||||
id: charge_done_binary
|
||||
|
||||
text_sensor:
|
||||
- platform: sy6970
|
||||
sy6970_id: sy6970_component
|
||||
bus_status:
|
||||
name: "Bus Status"
|
||||
id: bus_status_text
|
||||
charge_status:
|
||||
name: "Charge Status"
|
||||
id: charge_status_text
|
||||
ntc_status:
|
||||
name: "NTC Status"
|
||||
id: ntc_status_text
|
||||
@@ -1,4 +0,0 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
@@ -64,16 +64,3 @@ text_sensor:
|
||||
- suffix -> SUFFIX
|
||||
- map:
|
||||
- PREFIX text SUFFIX -> mapped
|
||||
|
||||
- platform: template
|
||||
name: "Test Lambda Filter"
|
||||
id: test_lambda_filter
|
||||
filters:
|
||||
- lambda: |-
|
||||
return {"[" + x + "]"};
|
||||
- to_upper
|
||||
- lambda: |-
|
||||
if (x.length() > 10) {
|
||||
return {x.substr(0, 10) + "..."};
|
||||
}
|
||||
return {x};
|
||||
|
||||
@@ -56,36 +56,6 @@ text_sensor:
|
||||
- prepend: "["
|
||||
- append: "]"
|
||||
|
||||
- platform: template
|
||||
name: "To Lower Sensor"
|
||||
id: to_lower_sensor
|
||||
filters:
|
||||
- to_lower
|
||||
|
||||
- platform: template
|
||||
name: "Lambda Sensor"
|
||||
id: lambda_sensor
|
||||
filters:
|
||||
- lambda: |-
|
||||
return {"[" + x + "]"};
|
||||
|
||||
- platform: template
|
||||
name: "Lambda Raw State Sensor"
|
||||
id: lambda_raw_state_sensor
|
||||
filters:
|
||||
- lambda: |-
|
||||
return {x + " MODIFIED"};
|
||||
|
||||
- platform: template
|
||||
name: "Lambda Skip Sensor"
|
||||
id: lambda_skip_sensor
|
||||
filters:
|
||||
- lambda: |-
|
||||
if (x == "skip") {
|
||||
return {};
|
||||
}
|
||||
return {x + " passed"};
|
||||
|
||||
# Button to publish values and log raw_state vs state
|
||||
button:
|
||||
- platform: template
|
||||
@@ -209,73 +179,3 @@ button:
|
||||
format: "CHAINED: state='%s'"
|
||||
args:
|
||||
- id(chained_sensor).state.c_str()
|
||||
|
||||
- platform: template
|
||||
name: "Test To Lower Button"
|
||||
id: test_to_lower_button
|
||||
on_press:
|
||||
- text_sensor.template.publish:
|
||||
id: to_lower_sensor
|
||||
state: "HELLO WORLD"
|
||||
- delay: 50ms
|
||||
- logger.log:
|
||||
format: "TO_LOWER: state='%s'"
|
||||
args:
|
||||
- id(to_lower_sensor).state.c_str()
|
||||
|
||||
- platform: template
|
||||
name: "Test Lambda Button"
|
||||
id: test_lambda_button
|
||||
on_press:
|
||||
- text_sensor.template.publish:
|
||||
id: lambda_sensor
|
||||
state: "test"
|
||||
- delay: 50ms
|
||||
- logger.log:
|
||||
format: "LAMBDA: state='%s'"
|
||||
args:
|
||||
- id(lambda_sensor).state.c_str()
|
||||
|
||||
- platform: template
|
||||
name: "Test Lambda Pass Button"
|
||||
id: test_lambda_pass_button
|
||||
on_press:
|
||||
- text_sensor.template.publish:
|
||||
id: lambda_skip_sensor
|
||||
state: "value"
|
||||
- delay: 50ms
|
||||
- logger.log:
|
||||
format: "LAMBDA_PASS: state='%s'"
|
||||
args:
|
||||
- id(lambda_skip_sensor).state.c_str()
|
||||
|
||||
- platform: template
|
||||
name: "Test Lambda Skip Button"
|
||||
id: test_lambda_skip_button
|
||||
on_press:
|
||||
- text_sensor.template.publish:
|
||||
id: lambda_skip_sensor
|
||||
state: "skip"
|
||||
- delay: 50ms
|
||||
# When lambda returns {}, the value should NOT be published
|
||||
# so state should remain from previous publish (or empty if first)
|
||||
- logger.log:
|
||||
format: "LAMBDA_SKIP: state='%s'"
|
||||
args:
|
||||
- id(lambda_skip_sensor).state.c_str()
|
||||
|
||||
- platform: template
|
||||
name: "Test Lambda Raw State Button"
|
||||
id: test_lambda_raw_state_button
|
||||
on_press:
|
||||
- text_sensor.template.publish:
|
||||
id: lambda_raw_state_sensor
|
||||
state: "original"
|
||||
- delay: 50ms
|
||||
# Verify raw_state is preserved (not mutated) after lambda filter
|
||||
# state should be "original MODIFIED", raw_state should be "original"
|
||||
- logger.log:
|
||||
format: "LAMBDA_RAW_STATE: state='%s' raw_state='%s'"
|
||||
args:
|
||||
- id(lambda_raw_state_sensor).state.c_str()
|
||||
- id(lambda_raw_state_sensor).get_raw_state().c_str()
|
||||
|
||||
@@ -42,11 +42,6 @@ async def test_text_sensor_raw_state(
|
||||
map_off_future: asyncio.Future[str] = loop.create_future()
|
||||
map_unknown_future: asyncio.Future[str] = loop.create_future()
|
||||
chained_future: asyncio.Future[str] = loop.create_future()
|
||||
to_lower_future: asyncio.Future[str] = loop.create_future()
|
||||
lambda_future: asyncio.Future[str] = loop.create_future()
|
||||
lambda_pass_future: asyncio.Future[str] = loop.create_future()
|
||||
lambda_skip_future: asyncio.Future[str] = loop.create_future()
|
||||
lambda_raw_state_future: asyncio.Future[tuple[str, str]] = loop.create_future()
|
||||
|
||||
# Patterns to match log output
|
||||
# NO_FILTER: state='hello world' raw_state='hello world'
|
||||
@@ -63,13 +58,6 @@ async def test_text_sensor_raw_state(
|
||||
map_off_pattern = re.compile(r"MAP_OFF: state='([^']*)'")
|
||||
map_unknown_pattern = re.compile(r"MAP_UNKNOWN: state='([^']*)'")
|
||||
chained_pattern = re.compile(r"CHAINED: state='([^']*)'")
|
||||
to_lower_pattern = re.compile(r"TO_LOWER: state='([^']*)'")
|
||||
lambda_pattern = re.compile(r"LAMBDA: state='([^']*)'")
|
||||
lambda_pass_pattern = re.compile(r"LAMBDA_PASS: state='([^']*)'")
|
||||
lambda_skip_pattern = re.compile(r"LAMBDA_SKIP: state='([^']*)'")
|
||||
lambda_raw_state_pattern = re.compile(
|
||||
r"LAMBDA_RAW_STATE: state='([^']*)' raw_state='([^']*)'"
|
||||
)
|
||||
|
||||
def check_output(line: str) -> None:
|
||||
"""Check log output for expected messages."""
|
||||
@@ -104,27 +92,6 @@ async def test_text_sensor_raw_state(
|
||||
if not chained_future.done() and (match := chained_pattern.search(line)):
|
||||
chained_future.set_result(match.group(1))
|
||||
|
||||
if not to_lower_future.done() and (match := to_lower_pattern.search(line)):
|
||||
to_lower_future.set_result(match.group(1))
|
||||
|
||||
if not lambda_future.done() and (match := lambda_pattern.search(line)):
|
||||
lambda_future.set_result(match.group(1))
|
||||
|
||||
if not lambda_pass_future.done() and (
|
||||
match := lambda_pass_pattern.search(line)
|
||||
):
|
||||
lambda_pass_future.set_result(match.group(1))
|
||||
|
||||
if not lambda_skip_future.done() and (
|
||||
match := lambda_skip_pattern.search(line)
|
||||
):
|
||||
lambda_skip_future.set_result(match.group(1))
|
||||
|
||||
if not lambda_raw_state_future.done() and (
|
||||
match := lambda_raw_state_pattern.search(line)
|
||||
):
|
||||
lambda_raw_state_future.set_result((match.group(1), match.group(2)))
|
||||
|
||||
async with (
|
||||
run_compiled(yaml_config, line_callback=check_output),
|
||||
api_client_connected() as client,
|
||||
@@ -305,111 +272,3 @@ async def test_text_sensor_raw_state(
|
||||
pytest.fail("Timeout waiting for CHAINED log message")
|
||||
|
||||
assert state == "[value]", f"Chained failed: expected '[value]', got '{state}'"
|
||||
|
||||
# Test 10: to_lower filter
|
||||
# "HELLO WORLD" -> "hello world"
|
||||
to_lower_button = next(
|
||||
(e for e in entities if "test_to_lower_button" in e.object_id.lower()),
|
||||
None,
|
||||
)
|
||||
assert to_lower_button is not None, "Test To Lower Button not found"
|
||||
client.button_command(to_lower_button.key)
|
||||
|
||||
try:
|
||||
state = await asyncio.wait_for(to_lower_future, timeout=5.0)
|
||||
except TimeoutError:
|
||||
pytest.fail("Timeout waiting for TO_LOWER log message")
|
||||
|
||||
assert state == "hello world", (
|
||||
f"to_lower failed: expected 'hello world', got '{state}'"
|
||||
)
|
||||
|
||||
# Test 11: Lambda filter
|
||||
# "test" -> "[test]"
|
||||
lambda_button = next(
|
||||
(e for e in entities if "test_lambda_button" in e.object_id.lower()),
|
||||
None,
|
||||
)
|
||||
assert lambda_button is not None, "Test Lambda Button not found"
|
||||
client.button_command(lambda_button.key)
|
||||
|
||||
try:
|
||||
state = await asyncio.wait_for(lambda_future, timeout=5.0)
|
||||
except TimeoutError:
|
||||
pytest.fail("Timeout waiting for LAMBDA log message")
|
||||
|
||||
assert state == "[test]", f"Lambda failed: expected '[test]', got '{state}'"
|
||||
|
||||
# Test 12: Lambda filter - value passes through
|
||||
# "value" -> "value passed"
|
||||
lambda_pass_button = next(
|
||||
(e for e in entities if "test_lambda_pass_button" in e.object_id.lower()),
|
||||
None,
|
||||
)
|
||||
assert lambda_pass_button is not None, "Test Lambda Pass Button not found"
|
||||
client.button_command(lambda_pass_button.key)
|
||||
|
||||
try:
|
||||
state = await asyncio.wait_for(lambda_pass_future, timeout=5.0)
|
||||
except TimeoutError:
|
||||
pytest.fail("Timeout waiting for LAMBDA_PASS log message")
|
||||
|
||||
assert state == "value passed", (
|
||||
f"Lambda pass failed: expected 'value passed', got '{state}'"
|
||||
)
|
||||
|
||||
# Test 13: Lambda filter - skip publishing (return {})
|
||||
# "skip" -> no publish, state remains "value passed" from previous test
|
||||
lambda_skip_button = next(
|
||||
(e for e in entities if "test_lambda_skip_button" in e.object_id.lower()),
|
||||
None,
|
||||
)
|
||||
assert lambda_skip_button is not None, "Test Lambda Skip Button not found"
|
||||
client.button_command(lambda_skip_button.key)
|
||||
|
||||
try:
|
||||
state = await asyncio.wait_for(lambda_skip_future, timeout=5.0)
|
||||
except TimeoutError:
|
||||
pytest.fail("Timeout waiting for LAMBDA_SKIP log message")
|
||||
|
||||
# When lambda returns {}, value should NOT be published
|
||||
# State remains from previous successful publish ("value passed")
|
||||
assert state == "value passed", (
|
||||
f"Lambda skip failed: expected 'value passed' (unchanged), got '{state}'"
|
||||
)
|
||||
|
||||
# Test 14: Lambda filter - verify raw_state is preserved (not mutated)
|
||||
# This is critical to verify the in-place mutation optimization is safe
|
||||
# "original" -> state="original MODIFIED", raw_state="original"
|
||||
lambda_raw_state_button = next(
|
||||
(
|
||||
e
|
||||
for e in entities
|
||||
if "test_lambda_raw_state_button" in e.object_id.lower()
|
||||
),
|
||||
None,
|
||||
)
|
||||
assert lambda_raw_state_button is not None, (
|
||||
"Test Lambda Raw State Button not found"
|
||||
)
|
||||
client.button_command(lambda_raw_state_button.key)
|
||||
|
||||
try:
|
||||
state, raw_state = await asyncio.wait_for(
|
||||
lambda_raw_state_future, timeout=5.0
|
||||
)
|
||||
except TimeoutError:
|
||||
pytest.fail("Timeout waiting for LAMBDA_RAW_STATE log message")
|
||||
|
||||
assert state == "original MODIFIED", (
|
||||
f"Lambda raw_state test failed: expected state='original MODIFIED', "
|
||||
f"got '{state}'"
|
||||
)
|
||||
assert raw_state == "original", (
|
||||
f"Lambda raw_state test failed: raw_state was mutated! "
|
||||
f"Expected 'original', got '{raw_state}'"
|
||||
)
|
||||
assert state != raw_state, (
|
||||
f"Lambda filter should modify state but preserve raw_state. "
|
||||
f"state='{state}', raw_state='{raw_state}'"
|
||||
)
|
||||
|
||||
@@ -192,20 +192,6 @@ def test_check_error_unexpected_response() -> None:
|
||||
espota2.check_error([0x7F], [espota2.RESPONSE_OK, espota2.RESPONSE_AUTH_OK])
|
||||
|
||||
|
||||
def test_check_error_empty_data() -> None:
|
||||
"""Test check_error raises error when device closes connection without responding."""
|
||||
with pytest.raises(
|
||||
espota2.OTAError, match="Device closed connection without responding"
|
||||
):
|
||||
espota2.check_error([], [espota2.RESPONSE_OK])
|
||||
|
||||
# Also test with empty bytes
|
||||
with pytest.raises(
|
||||
espota2.OTAError, match="Device closed connection without responding"
|
||||
):
|
||||
espota2.check_error(b"", [espota2.RESPONSE_OK])
|
||||
|
||||
|
||||
def test_send_check_with_various_data_types(mock_socket: Mock) -> None:
|
||||
"""Test send_check handles different data types."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user